diff --git a/.qmake.conf b/.qmake.conf index 9c25b4d0b55..fe958c0b81c 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -6,4 +6,4 @@ DEFINES += QT_NO_JAVA_STYLE_ITERATORS QT_SOURCE_TREE = $$PWD QT_BUILD_TREE = $$shadowed($$PWD) -MODULE_VERSION = 5.15.15 +MODULE_VERSION = 5.15.16 diff --git a/mkspecs/features/qt_module.prf b/mkspecs/features/qt_module.prf index a9c35726764..79187cb9932 100644 --- a/mkspecs/features/qt_module.prf +++ b/mkspecs/features/qt_module.prf @@ -233,15 +233,8 @@ android: CONFIG += qt_android_deps no_linker_version_script current = Qt_$$QT_MAJOR_VERSION verscript_content += "$$current { *; };" - isEmpty(QT_NAMESPACE): tag_symbol = qt_version_tag - else: tag_symbol = qt_version_tag_$$QT_NAMESPACE - for(i, 0..$$QT_MINOR_VERSION) { - previous = $$current - current = Qt_$${QT_MAJOR_VERSION}.$$i - equals(i, $$QT_MINOR_VERSION): verscript_content += "$$current { $$tag_symbol; } $$previous;" - else: verscript_content += "$$current {} $$previous;" - } + !isEmpty(MODULE_VERSCRIPT_CONTENT_EXT): verscript_content += $$MODULE_VERSCRIPT_CONTENT_EXT # Add a post-processing step to replace the @FILE:filename@ verscript_in = $${verscript}.in diff --git a/mkspecs/features/toolchain.prf b/mkspecs/features/toolchain.prf index 0c505fc9659..c70f2797c8d 100644 --- a/mkspecs/features/toolchain.prf +++ b/mkspecs/features/toolchain.prf @@ -288,9 +288,12 @@ isEmpty($${target_prefix}.INCDIRS) { } } } - isEmpty(QMAKE_DEFAULT_LIBDIRS)|isEmpty(QMAKE_DEFAULT_INCDIRS): \ + isEmpty(QMAKE_DEFAULT_INCDIRS): \ !integrity: \ - error("failed to parse default search paths from compiler output") + error("failed to parse default include paths from compiler output") + isEmpty(QMAKE_DEFAULT_LIBDIRS): \ + !integrity:!darwin: \ + error("failed to parse default library paths from compiler output") QMAKE_DEFAULT_LIBDIRS = $$unique(QMAKE_DEFAULT_LIBDIRS) } else: ghs { cmd = $$QMAKE_CXX $$QMAKE_CXXFLAGS -$${LITERAL_HASH} -o /tmp/fake_output /tmp/fake_input.cpp @@ -412,7 +415,7 @@ isEmpty($${target_prefix}.INCDIRS) { QMAKE_DEFAULT_INCDIRS = $$split(INCLUDE, $$QMAKE_DIRLIST_SEP) } - unix:if(!cross_compile|host_build) { + unix:!darwin:if(!cross_compile|host_build) { isEmpty(QMAKE_DEFAULT_INCDIRS): QMAKE_DEFAULT_INCDIRS = /usr/include /usr/local/include isEmpty(QMAKE_DEFAULT_LIBDIRS): QMAKE_DEFAULT_LIBDIRS = /lib /usr/lib } diff --git a/src/3rdparty/freetype/README b/src/3rdparty/freetype/README index eb48e7e1848..cd4c1d7d114 100644 --- a/src/3rdparty/freetype/README +++ b/src/3rdparty/freetype/README @@ -1,4 +1,4 @@ -FreeType 2.13.1 +FreeType 2.13.2 =============== Homepage: https://www.freetype.org @@ -32,9 +32,9 @@ sites. Go to and download one of the following files. - freetype-doc-2.13.1.tar.xz - freetype-doc-2.13.1.tar.gz - ftdoc2131.zip + freetype-doc-2.13.2.tar.xz + freetype-doc-2.13.2.tar.gz + ftdoc2132.zip To view the documentation online, go to diff --git a/src/3rdparty/freetype/docs/CHANGES b/src/3rdparty/freetype/docs/CHANGES index b6ad1ce5df8..96cf607d70d 100644 --- a/src/3rdparty/freetype/docs/CHANGES +++ b/src/3rdparty/freetype/docs/CHANGES @@ -1,3 +1,17 @@ +CHANGES BETWEEN 2.13.1 and 2.13.2 (2023-Aug-25) + + I. MISCELLANEOUS + + - Better support for CFF2 variation fonts. + + - TrueType interpreter version 38 (also known as 'Infinality') has + been removed. + + - Improved OpenVMS support. + + +====================================================================== + CHANGES BETWEEN 2.13.0 and 2.13.1 (2023-Jun-24) I. MISCELLANEOUS diff --git a/src/3rdparty/freetype/include/dlg/dlg.h b/src/3rdparty/freetype/include/dlg/dlg.h index 3a7abf8f051..fa10730e83a 100644 --- a/src/3rdparty/freetype/include/dlg/dlg.h +++ b/src/3rdparty/freetype/include/dlg/dlg.h @@ -85,6 +85,13 @@ #endif #endif +// This macro is used when an assertion fails. It gets the source expression +// and can return an alternative (that must stay alive). +// Mainly useful to execute something on failed assertion. +#ifndef DLG_FAILED_ASSERTION_TEXT + #define DLG_FAILED_ASSERTION_TEXT(x) x +#endif + // - utility - // two methods needed since cplusplus does not support compound literals // and c does not support uniform initialization/initializer lists @@ -131,83 +138,12 @@ struct dlg_origin { // Type of the output handler, see dlg_set_handler. typedef void(*dlg_handler)(const struct dlg_origin* origin, const char* string, void* data); -#ifdef DLG_DISABLE +#ifndef DLG_DISABLE // Tagged/Untagged logging with variable level // Tags must always be in the format `("tag1", "tag2")` (including brackets) - #define dlg_log(level, ...) - #define dlg_logt(level, tags, ...) - - // Dynamic level assert macros in various versions for additional arguments - #define dlg_assertl(level, expr) // assert without tags/message - #define dlg_assertlt(level, tags, expr) // assert with tags - #define dlg_assertlm(level, expr, ...) // assert with message - #define dlg_assertltm(level, tags, expr, ...) // assert with tags & message - - // Sets the handler that is responsible for formatting and outputting log calls. - // This function is not thread safe and the handler is set globally. - // The handler itself must not change dlg tags or call a dlg macro (if it - // does so, the provided string or tags array in 'origin' might get invalid). - // The handler can also be used for various other things such as dealing - // with failed assertions or filtering calls based on the passed tags. - // The default handler is dlg_default_output (see its doc for more info). - // If using c++ make sure the registered handler cannot throw e.g. by - // wrapping everything into a try-catch blog. - inline void dlg_set_handler(dlg_handler handler, void* data) { - (void) handler; - (void) data; - } - - // Returns the currently active dlg handler and sets `data` to - // its user data pointer. `data` must not be NULL. - // Useful to create handler chains. - // This function is not threadsafe, i.e. retrieving the handler while - // changing it from another thread is unsafe. - // See `dlg_set_handler`. - inline dlg_handler dlg_get_handler(void** data) { - *data = NULL; - return NULL; - } - - // The default output handler. - // Only use this to reset the output handler, prefer to use - // dlg_generic_output (from output.h) which this function simply calls. - // It also flushes the stream used and correctly outputs even from multiple threads. - inline void dlg_default_output(const struct dlg_origin* o, const char* str, void* data) { - (void) o; - (void) str; - (void) data; - } - - // Adds the given tag associated with the given function to the thread specific list. - // If func is not NULL the tag will only applied to calls from the same function. - // Remove the tag again calling dlg_remove_tag (with exactly the same pointers!). - // Does not check if the tag is already present. - inline void dlg_add_tag(const char* tag, const char* func) { - (void) tag; - (void) func; - } - - // Removes a tag added with dlg_add_tag (has no effect for tags no present). - // The pointers must be exactly the same pointers that were supplied to dlg_add_tag, - // this function will not check using strcmp. When the same tag/func combination - // is added multiple times, this function remove exactly one candidate, it is - // undefined which. Returns whether a tag was found (and removed). - inline bool dlg_remove_tag(const char* tag, const char* func) { - (void) tag; - (void) func; - return true; - } - - // Returns the thread-specific buffer and its size for dlg. - // The buffer should only be used by formatting functions. - // The buffer can be reallocated and the size changed, just make sure - // to update both values correctly. - inline char** dlg_thread_buffer(size_t** size) { - (void) size; - return NULL; - } - -#else // DLG_DISABLE + // Example usages: + // dlg_log(dlg_level_warning, "test 1") + // dlg_logt(("tag1, "tag2"), dlg_level_debug, "test %d", 2) #define dlg_log(level, ...) if(level >= DLG_LOG_LEVEL) \ dlg__do_log(level, DLG_CREATE_TAGS(NULL), DLG_FILE, __LINE__, __func__, \ DLG_FMT_FUNC(__VA_ARGS__), NULL) @@ -215,23 +151,31 @@ typedef void(*dlg_handler)(const struct dlg_origin* origin, const char* string, dlg__do_log(level, DLG_CREATE_TAGS tags, DLG_FILE, __LINE__, __func__, \ DLG_FMT_FUNC(__VA_ARGS__), NULL) + // Dynamic level assert macros in various versions for additional arguments + // Example usages: + // dlg_assertl(dlg_level_warning, data != nullptr); + // dlg_assertlt(("tag1, "tag2"), dlg_level_trace, data != nullptr); + // dlg_asserttlm(("tag1), dlg_level_warning, data != nullptr, "Data must not be null"); + // dlg_assertlm(dlg_level_error, data != nullptr, "Data must not be null"); #define dlg_assertl(level, expr) if(level >= DLG_ASSERT_LEVEL && !(expr)) \ - dlg__do_log(level, DLG_CREATE_TAGS(NULL), DLG_FILE, __LINE__, __func__, NULL, #expr) + dlg__do_log(level, DLG_CREATE_TAGS(NULL), DLG_FILE, __LINE__, __func__, NULL, \ + DLG_FAILED_ASSERTION_TEXT(#expr)) #define dlg_assertlt(level, tags, expr) if(level >= DLG_ASSERT_LEVEL && !(expr)) \ - dlg__do_log(level, DLG_CREATE_TAGS tags, DLG_FILE, __LINE__, __func__, NULL, #expr) + dlg__do_log(level, DLG_CREATE_TAGS tags, DLG_FILE, __LINE__, __func__, NULL, \ + DLG_FAILED_ASSERTION_TEXT(#expr)) #define dlg_assertlm(level, expr, ...) if(level >= DLG_ASSERT_LEVEL && !(expr)) \ dlg__do_log(level, DLG_CREATE_TAGS(NULL), DLG_FILE, __LINE__, __func__, \ - DLG_FMT_FUNC(__VA_ARGS__), #expr) + DLG_FMT_FUNC(__VA_ARGS__), DLG_FAILED_ASSERTION_TEXT(#expr)) #define dlg_assertltm(level, tags, expr, ...) if(level >= DLG_ASSERT_LEVEL && !(expr)) \ dlg__do_log(level, DLG_CREATE_TAGS tags, DLG_FILE, __LINE__, \ - __func__, DLG_FMT_FUNC(__VA_ARGS__), #expr) + __func__, DLG_FMT_FUNC(__VA_ARGS__), DLG_FAILED_ASSERTION_TEXT(#expr)) - DLG_API void dlg_set_handler(dlg_handler handler, void* data); - DLG_API dlg_handler dlg_get_handler(void** data); - DLG_API void dlg_default_output(const struct dlg_origin*, const char* string, void*); - DLG_API void dlg_add_tag(const char* tag, const char* func); - DLG_API bool dlg_remove_tag(const char* tag, const char* func); - DLG_API char** dlg_thread_buffer(size_t** size); + #define dlg__assert_or(level, tags, expr, code, msg) if(!(expr)) {\ + if(level >= DLG_ASSERT_LEVEL) \ + dlg__do_log(level, tags, DLG_FILE, __LINE__, __func__, msg, \ + DLG_FAILED_ASSERTION_TEXT(#expr)); \ + code; \ + } (void) NULL // - Private interface: not part of the abi/api but needed in macros - // Formats the given format string and arguments as printf would, uses the thread buffer. @@ -239,8 +183,66 @@ typedef void(*dlg_handler)(const struct dlg_origin* origin, const char* string, DLG_API void dlg__do_log(enum dlg_level lvl, const char* const*, const char*, int, const char*, const char*, const char*); DLG_API const char* dlg__strip_root_path(const char* file, const char* base); + +#else // DLG_DISABLE + + #define dlg_log(level, ...) + #define dlg_logt(level, tags, ...) + + #define dlg_assertl(level, expr) // assert without tags/message + #define dlg_assertlt(level, tags, expr) // assert with tags + #define dlg_assertlm(level, expr, ...) // assert with message + #define dlg_assertltm(level, tags, expr, ...) // assert with tags & message + + #define dlg__assert_or(level, tags, expr, code, msg) if(!(expr)) { code; } (void) NULL #endif // DLG_DISABLE +// The API below is independent from DLG_DISABLE + +// Sets the handler that is responsible for formatting and outputting log calls. +// This function is not thread safe and the handler is set globally. +// The handler itself must not change dlg tags or call a dlg macro (if it +// does so, the provided string or tags array in 'origin' might get invalid). +// The handler can also be used for various other things such as dealing +// with failed assertions or filtering calls based on the passed tags. +// The default handler is dlg_default_output (see its doc for more info). +// If using c++ make sure the registered handler cannot throw e.g. by +// wrapping everything into a try-catch blog. +DLG_API void dlg_set_handler(dlg_handler handler, void* data); + +// The default output handler. +// Only use this to reset the output handler, prefer to use +// dlg_generic_output (from output.h) which this function simply calls. +// It also flushes the stream used and correctly outputs even from multiple threads. +DLG_API void dlg_default_output(const struct dlg_origin*, const char* string, void*); + +// Returns the currently active dlg handler and sets `data` to +// its user data pointer. `data` must not be NULL. +// Useful to create handler chains. +// This function is not threadsafe, i.e. retrieving the handler while +// changing it from another thread is unsafe. +// See `dlg_set_handler`. +DLG_API dlg_handler dlg_get_handler(void** data); + +// Adds the given tag associated with the given function to the thread specific list. +// If func is not NULL the tag will only applied to calls from the same function. +// Remove the tag again calling dlg_remove_tag (with exactly the same pointers!). +// Does not check if the tag is already present. +DLG_API void dlg_add_tag(const char* tag, const char* func); + +// Removes a tag added with dlg_add_tag (has no effect for tags no present). +// The pointers must be exactly the same pointers that were supplied to dlg_add_tag, +// this function will not check using strcmp. When the same tag/func combination +// is added multiple times, this function remove exactly one candidate, it is +// undefined which. Returns whether a tag was found (and removed). +DLG_API bool dlg_remove_tag(const char* tag, const char* func); + +// Returns the thread-specific buffer and its size for dlg. +// The buffer should only be used by formatting functions. +// The buffer can be reallocated and the size changed, just make sure +// to update both values correctly. +DLG_API char** dlg_thread_buffer(size_t** size); + // Untagged leveled logging #define dlg_trace(...) dlg_log(dlg_level_trace, __VA_ARGS__) #define dlg_debug(...) dlg_log(dlg_level_debug, __VA_ARGS__) @@ -263,6 +265,24 @@ typedef void(*dlg_handler)(const struct dlg_origin* origin, const char* string, #define dlg_assertm(expr, ...) dlg_assertlm(DLG_DEFAULT_ASSERT, expr, __VA_ARGS__) #define dlg_asserttm(tags, expr, ...) dlg_assertltm(DLG_DEFAULT_ASSERT, tags, expr, __VA_ARGS__) +// If (expr) does not evaluate to true, always executes 'code' (no matter what +// DLG_ASSERT_LEVEL is or if dlg is disabled or not). +// When dlg is enabled and the level is greater or equal to DLG_ASSERT_LEVEL, +// logs the failed assertion. +// Example usages: +// dlg_assertl_or(dlg_level_warn, data != nullptr, return); +// dlg_assertlm_or(dlg_level_fatal, data != nullptr, return, "Data must not be null"); +// dlg_assert_or(data != nullptr, logError(); return false); +#define dlg_assertltm_or(level, tags, expr, code, ...) dlg__assert_or(level, \ + DLG_CREATE_TAGS tags, expr, code, DLG_FMT_FUNC(__VA_ARGS__)) +#define dlg_assertlm_or(level, expr, code, ...) dlg__assert_or(level, \ + DLG_CREATE_TAGS(NULL), expr, code, DLG_FMT_FUNC(__VA_ARGS__)) +#define dlg_assertl_or(level, expr, code) dlg__assert_or(level, \ + DLG_CREATE_TAGS(NULL), expr, code, NULL) + +#define dlg_assert_or(expr, code) dlg_assertl_or(DLG_DEFAULT_ASSERT, expr, code) +#define dlg_assertm_or(expr, code, ...) dlg_assertlm_or(DLG_DEFAULT_ASSERT, expr, code, __VA_ARGS__) + #ifdef __cplusplus } #endif diff --git a/src/3rdparty/freetype/include/freetype/freetype.h b/src/3rdparty/freetype/include/freetype/freetype.h index 4a074a4449b..92acf3794a7 100644 --- a/src/3rdparty/freetype/include/freetype/freetype.h +++ b/src/3rdparty/freetype/include/freetype/freetype.h @@ -5222,7 +5222,7 @@ FT_BEGIN_HEADER */ #define FREETYPE_MAJOR 2 #define FREETYPE_MINOR 13 -#define FREETYPE_PATCH 1 +#define FREETYPE_PATCH 2 /************************************************************************** diff --git a/src/3rdparty/freetype/include/freetype/internal/ftcalc.h b/src/3rdparty/freetype/include/freetype/internal/ftcalc.h index d1baa392bd6..d9aea236024 100644 --- a/src/3rdparty/freetype/include/freetype/internal/ftcalc.h +++ b/src/3rdparty/freetype/include/freetype/internal/ftcalc.h @@ -332,9 +332,9 @@ FT_BEGIN_HEADER * Based on geometric considerations we use the following inequality to * identify a degenerate matrix. * - * 50 * abs(xx*yy - xy*yx) < xx^2 + xy^2 + yx^2 + yy^2 + * 32 * abs(xx*yy - xy*yx) < xx^2 + xy^2 + yx^2 + yy^2 * - * Value 50 is heuristic. + * Value 32 is heuristic. */ FT_BASE( FT_Bool ) FT_Matrix_Check( const FT_Matrix* matrix ); diff --git a/src/3rdparty/freetype/include/freetype/internal/tttypes.h b/src/3rdparty/freetype/include/freetype/internal/tttypes.h index 984121a0e48..b9788c7831e 100644 --- a/src/3rdparty/freetype/include/freetype/internal/tttypes.h +++ b/src/3rdparty/freetype/include/freetype/internal/tttypes.h @@ -1394,14 +1394,6 @@ FT_BEGIN_HEADER * vert_metrics_offset :: * The file offset of the 'vmtx' table. * - * sph_found_func_flags :: - * Flags identifying special bytecode functions (used by the v38 - * implementation of the bytecode interpreter). - * - * sph_compatibility_mode :: - * This flag is set if we are in ClearType backward compatibility mode - * (used by the v38 implementation of the bytecode interpreter). - * * ebdt_start :: * The file offset of the sbit data table (CBDT, bdat, etc.). * @@ -1597,13 +1589,6 @@ FT_BEGIN_HEADER FT_ULong horz_metrics_offset; FT_ULong vert_metrics_offset; -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - /* since 2.4.12 */ - FT_ULong sph_found_func_flags; /* special functions found */ - /* for this face */ - FT_Bool sph_compatibility_mode; -#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ - #ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS /* since 2.7 */ FT_ULong ebdt_start; /* either `CBDT', `EBDT', or `bdat' */ diff --git a/src/3rdparty/freetype/qt_attribution.json b/src/3rdparty/freetype/qt_attribution.json index abfc43aaf1f..66443dee1e1 100644 --- a/src/3rdparty/freetype/qt_attribution.json +++ b/src/3rdparty/freetype/qt_attribution.json @@ -7,8 +7,7 @@ "Description": "FreeType is a freely available software library to render fonts.", "Homepage": "http://www.freetype.org", - "Version": "2.13.0", - + "Version": "2.13.2", "License": "Freetype Project License or GNU General Public License v2.0 only", "LicenseId": "FTL OR GPL-2.0", "LicenseFile": "LICENSE.txt", diff --git a/src/3rdparty/freetype/src/autofit/afcjk.c b/src/3rdparty/freetype/src/autofit/afcjk.c index af775b190c0..f414289adcd 100644 --- a/src/3rdparty/freetype/src/autofit/afcjk.c +++ b/src/3rdparty/freetype/src/autofit/afcjk.c @@ -1634,7 +1634,7 @@ stem_edge->pos = base_edge->pos + fitted_width; - FT_TRACE5(( " CJKLINK: edge %ld @%d (opos=%.2f) linked to %.2f," + FT_TRACE5(( " CJKLINK: edge %td @%d (opos=%.2f) linked to %.2f," " dist was %.2f, now %.2f\n", stem_edge - hints->axis[dim].edges, stem_edge->fpos, (double)stem_edge->opos / 64, @@ -1858,7 +1858,7 @@ continue; #ifdef FT_DEBUG_LEVEL_TRACE - FT_TRACE5(( " CJKBLUE: edge %ld @%d (opos=%.2f) snapped to %.2f," + FT_TRACE5(( " CJKBLUE: edge %td @%d (opos=%.2f) snapped to %.2f," " was %.2f\n", edge1 - edges, edge1->fpos, (double)edge1->opos / 64, (double)blue->fit / 64, (double)edge1->pos / 64 )); @@ -1922,7 +1922,7 @@ /* this should not happen, but it's better to be safe */ if ( edge2->blue_edge ) { - FT_TRACE5(( "ASSERTION FAILED for edge %ld\n", edge2-edges )); + FT_TRACE5(( "ASSERTION FAILED for edge %td\n", edge2 - edges )); af_cjk_align_linked_edge( hints, dim, edge2, edge ); edge->flags |= AF_EDGE_DONE; diff --git a/src/3rdparty/freetype/src/autofit/aflatin.c b/src/3rdparty/freetype/src/autofit/aflatin.c index 46c6e450a85..b86367aa94d 100644 --- a/src/3rdparty/freetype/src/autofit/aflatin.c +++ b/src/3rdparty/freetype/src/autofit/aflatin.c @@ -1022,7 +1022,7 @@ { *a = *b; FT_TRACE5(( "blue zone overlap:" - " adjusting %s %ld to %ld\n", + " adjusting %s %td to %ld\n", a_is_top ? "overshoot" : "reference", blue_sorted[i] - axis->blues, *a )); @@ -2960,7 +2960,7 @@ stem_edge->pos = base_edge->pos + fitted_width; - FT_TRACE5(( " LINK: edge %ld (opos=%.2f) linked to %.2f," + FT_TRACE5(( " LINK: edge %td (opos=%.2f) linked to %.2f," " dist was %.2f, now %.2f\n", stem_edge - hints->axis[dim].edges, (double)stem_edge->opos / 64, (double)stem_edge->pos / 64, @@ -3085,13 +3085,13 @@ #ifdef FT_DEBUG_LEVEL_TRACE if ( !anchor ) - FT_TRACE5(( " BLUE_ANCHOR: edge %ld (opos=%.2f) snapped to %.2f," - " was %.2f (anchor=edge %ld)\n", + FT_TRACE5(( " BLUE_ANCHOR: edge %td (opos=%.2f) snapped to %.2f," + " was %.2f (anchor=edge %td)\n", edge1 - edges, (double)edge1->opos / 64, (double)blue->fit / 64, (double)edge1->pos / 64, edge - edges )); else - FT_TRACE5(( " BLUE: edge %ld (opos=%.2f) snapped to %.2f," + FT_TRACE5(( " BLUE: edge %td (opos=%.2f) snapped to %.2f," " was %.2f\n", edge1 - edges, (double)edge1->opos / 64, (double)blue->fit / 64, @@ -3141,7 +3141,7 @@ /* this should not happen, but it's better to be safe */ if ( edge2->blue_edge ) { - FT_TRACE5(( " ASSERTION FAILED for edge %ld\n", edge2 - edges )); + FT_TRACE5(( " ASSERTION FAILED for edge %td\n", edge2 - edges )); af_latin_align_linked_edge( hints, dim, edge2, edge ); edge->flags |= AF_EDGE_DONE; @@ -3209,7 +3209,7 @@ anchor = edge; edge->flags |= AF_EDGE_DONE; - FT_TRACE5(( " ANCHOR: edge %ld (opos=%.2f) and %ld (opos=%.2f)" + FT_TRACE5(( " ANCHOR: edge %td (opos=%.2f) and %td (opos=%.2f)" " snapped to %.2f and %.2f\n", edge - edges, (double)edge->opos / 64, edge2 - edges, (double)edge2->opos / 64, @@ -3238,7 +3238,7 @@ if ( edge2->flags & AF_EDGE_DONE ) { - FT_TRACE5(( " ADJUST: edge %ld (pos=%.2f) moved to %.2f\n", + FT_TRACE5(( " ADJUST: edge %td (pos=%.2f) moved to %.2f\n", edge - edges, (double)edge->pos / 64, (double)( edge2->pos - cur_len ) / 64 )); @@ -3279,7 +3279,7 @@ edge->pos = cur_pos1 - cur_len / 2; edge2->pos = cur_pos1 + cur_len / 2; - FT_TRACE5(( " STEM: edge %ld (opos=%.2f) linked to %ld (opos=%.2f)" + FT_TRACE5(( " STEM: edge %td (opos=%.2f) linked to %td (opos=%.2f)" " snapped to %.2f and %.2f\n", edge - edges, (double)edge->opos / 64, edge2 - edges, (double)edge2->opos / 64, @@ -3310,7 +3310,7 @@ edge->pos = ( delta1 < delta2 ) ? cur_pos1 : cur_pos2; edge2->pos = edge->pos + cur_len; - FT_TRACE5(( " STEM: edge %ld (opos=%.2f) linked to %ld (opos=%.2f)" + FT_TRACE5(( " STEM: edge %td (opos=%.2f) linked to %td (opos=%.2f)" " snapped to %.2f and %.2f\n", edge - edges, (double)edge->opos / 64, edge2 - edges, (double)edge2->opos / 64, @@ -3333,7 +3333,7 @@ if ( edge->link && FT_ABS( edge->link->pos - edge[-1].pos ) > 16 ) { #ifdef FT_DEBUG_LEVEL_TRACE - FT_TRACE5(( " BOUND: edge %ld (pos=%.2f) moved to %.2f\n", + FT_TRACE5(( " BOUND: edge %td (pos=%.2f) moved to %.2f\n", edge - edges, (double)edge->pos / 64, (double)edge[-1].pos / 64 )); @@ -3435,7 +3435,7 @@ if ( delta < 64 + 16 ) { af_latin_align_serif_edge( hints, edge->serif, edge ); - FT_TRACE5(( " SERIF: edge %ld (opos=%.2f) serif to %ld (opos=%.2f)" + FT_TRACE5(( " SERIF: edge %td (opos=%.2f) serif to %td (opos=%.2f)" " aligned to %.2f\n", edge - edges, (double)edge->opos / 64, edge->serif - edges, (double)edge->serif->opos / 64, @@ -3445,9 +3445,9 @@ { edge->pos = FT_PIX_ROUND( edge->opos ); anchor = edge; - FT_TRACE5(( " SERIF_ANCHOR: edge %ld (opos=%.2f)" + FT_TRACE5(( " SERIF_ANCHOR: edge %td (opos=%.2f)" " snapped to %.2f\n", - edge-edges, + edge - edges, (double)edge->opos / 64, (double)edge->pos / 64 )); } else @@ -3474,8 +3474,8 @@ after->pos - before->pos, after->opos - before->opos ); - FT_TRACE5(( " SERIF_LINK1: edge %ld (opos=%.2f) snapped to %.2f" - " from %ld (opos=%.2f)\n", + FT_TRACE5(( " SERIF_LINK1: edge %td (opos=%.2f) snapped to %.2f" + " from %td (opos=%.2f)\n", edge - edges, (double)edge->opos / 64, (double)edge->pos / 64, before - edges, (double)before->opos / 64 )); @@ -3484,7 +3484,7 @@ { edge->pos = anchor->pos + ( ( edge->opos - anchor->opos + 16 ) & ~31 ); - FT_TRACE5(( " SERIF_LINK2: edge %ld (opos=%.2f)" + FT_TRACE5(( " SERIF_LINK2: edge %td (opos=%.2f)" " snapped to %.2f\n", edge - edges, (double)edge->opos / 64, (double)edge->pos / 64 )); @@ -3505,7 +3505,7 @@ if ( edge->link && FT_ABS( edge->link->pos - edge[-1].pos ) > 16 ) { #ifdef FT_DEBUG_LEVEL_TRACE - FT_TRACE5(( " BOUND: edge %ld (pos=%.2f) moved to %.2f\n", + FT_TRACE5(( " BOUND: edge %td (pos=%.2f) moved to %.2f\n", edge - edges, (double)edge->pos / 64, (double)edge[-1].pos / 64 )); @@ -3526,7 +3526,7 @@ if ( edge->link && FT_ABS( edge->link->pos - edge[-1].pos ) > 16 ) { #ifdef FT_DEBUG_LEVEL_TRACE - FT_TRACE5(( " BOUND: edge %ld (pos=%.2f) moved to %.2f\n", + FT_TRACE5(( " BOUND: edge %td (pos=%.2f) moved to %.2f\n", edge - edges, (double)edge->pos / 64, (double)edge[1].pos / 64 )); diff --git a/src/3rdparty/freetype/src/base/ftcalc.c b/src/3rdparty/freetype/src/base/ftcalc.c index 442b08ddf9d..c5bc7e3b14e 100644 --- a/src/3rdparty/freetype/src/base/ftcalc.c +++ b/src/3rdparty/freetype/src/base/ftcalc.c @@ -749,65 +749,43 @@ FT_BASE_DEF( FT_Bool ) FT_Matrix_Check( const FT_Matrix* matrix ) { - FT_Matrix m; - FT_Fixed val[4]; - FT_Fixed nonzero_minval, maxval; - FT_Fixed temp1, temp2; - FT_UInt i; + FT_Fixed xx, xy, yx, yy; + FT_Fixed val; + FT_Int shift; + FT_ULong temp1, temp2; if ( !matrix ) return 0; - val[0] = FT_ABS( matrix->xx ); - val[1] = FT_ABS( matrix->xy ); - val[2] = FT_ABS( matrix->yx ); - val[3] = FT_ABS( matrix->yy ); + xx = matrix->xx; + xy = matrix->xy; + yx = matrix->yx; + yy = matrix->yy; + val = FT_ABS( xx ) | FT_ABS( xy ) | FT_ABS( yx ) | FT_ABS( yy ); - /* - * To avoid overflow, we ensure that each value is not larger than - * - * int(sqrt(2^31 / 4)) = 23170 ; - * - * we also check that no value becomes zero if we have to scale. - */ - - maxval = 0; - nonzero_minval = FT_LONG_MAX; - - for ( i = 0; i < 4; i++ ) - { - if ( val[i] > maxval ) - maxval = val[i]; - if ( val[i] && val[i] < nonzero_minval ) - nonzero_minval = val[i]; - } - - /* we only handle 32bit values */ - if ( maxval > 0x7FFFFFFFL ) + /* we only handle non-zero 32-bit values */ + if ( !val || val > 0x7FFFFFFFL ) return 0; - if ( maxval > 23170 ) + /* Scale matrix to avoid the temp1 overflow, which is */ + /* more stringent than avoiding the temp2 overflow. */ + + shift = FT_MSB( val ) - 12; + + if ( shift > 0 ) { - FT_Fixed scale = FT_DivFix( maxval, 23170 ); - - - if ( !FT_DivFix( nonzero_minval, scale ) ) - return 0; /* value range too large */ - - m.xx = FT_DivFix( matrix->xx, scale ); - m.xy = FT_DivFix( matrix->xy, scale ); - m.yx = FT_DivFix( matrix->yx, scale ); - m.yy = FT_DivFix( matrix->yy, scale ); + xx >>= shift; + xy >>= shift; + yx >>= shift; + yy >>= shift; } - else - m = *matrix; - temp1 = FT_ABS( m.xx * m.yy - m.xy * m.yx ); - temp2 = m.xx * m.xx + m.xy * m.xy + m.yx * m.yx + m.yy * m.yy; + temp1 = 32U * (FT_ULong)FT_ABS( xx * yy - xy * yx ); + temp2 = (FT_ULong)( xx * xx ) + (FT_ULong)( xy * xy ) + + (FT_ULong)( yx * yx ) + (FT_ULong)( yy * yy ); - if ( temp1 == 0 || - temp2 / temp1 > 50 ) + if ( temp1 <= temp2 ) return 0; return 1; @@ -1092,9 +1070,6 @@ { FT_UInt i; FT_Int64 temp; -#ifndef FT_INT64 - FT_Int64 halfUnit; -#endif #ifdef FT_INT64 @@ -1139,13 +1114,10 @@ FT_Add64( &temp, &multResult, &temp ); } - /* Round value. */ - halfUnit.hi = 0; - halfUnit.lo = 0x8000; - FT_Add64( &temp, &halfUnit, &temp ); + /* Shift and round value. */ + return (FT_Int32)( ( ( temp.hi << 16 ) | ( temp.lo >> 16 ) ) + + ( 1 & ( temp.lo >> 15 ) ) ); - return (FT_Int32)( ( (FT_Int32)( temp.hi & 0xFFFF ) << 16 ) | - ( temp.lo >> 16 ) ); #endif /* !FT_INT64 */ diff --git a/src/3rdparty/freetype/src/base/ftobjs.c b/src/3rdparty/freetype/src/base/ftobjs.c index abfa3ab0e6e..89a25bc732d 100644 --- a/src/3rdparty/freetype/src/base/ftobjs.c +++ b/src/3rdparty/freetype/src/base/ftobjs.c @@ -1747,7 +1747,8 @@ FT_Memory memory = library->memory; - args.flags = 0; + args.driver = NULL; + args.flags = 0; if ( driver_name ) { diff --git a/src/3rdparty/freetype/src/base/ftstream.c b/src/3rdparty/freetype/src/base/ftstream.c index 05c5637578b..64826acebe5 100644 --- a/src/3rdparty/freetype/src/base/ftstream.c +++ b/src/3rdparty/freetype/src/base/ftstream.c @@ -141,7 +141,9 @@ if ( read_bytes > count ) read_bytes = count; - FT_MEM_COPY( buffer, stream->base + pos, read_bytes ); + /* Allow "reading" zero bytes without UB even if buffer is NULL */ + if ( count ) + FT_MEM_COPY( buffer, stream->base + pos, read_bytes ); } stream->pos = pos + read_bytes; @@ -178,7 +180,9 @@ if ( read_bytes > count ) read_bytes = count; - FT_MEM_COPY( buffer, stream->base + stream->pos, read_bytes ); + /* Allow "reading" zero bytes without UB even if buffer is NULL */ + if ( count ) + FT_MEM_COPY( buffer, stream->base + stream->pos, read_bytes ); } stream->pos += read_bytes; diff --git a/src/3rdparty/freetype/src/base/ftver.rc b/src/3rdparty/freetype/src/base/ftver.rc index c7155d53d77..137a6334b75 100644 --- a/src/3rdparty/freetype/src/base/ftver.rc +++ b/src/3rdparty/freetype/src/base/ftver.rc @@ -18,8 +18,8 @@ #include -#define FT_VERSION 2,13,1,0 -#define FT_VERSION_STR "2.13.1" +#define FT_VERSION 2,13,2,0 +#define FT_VERSION_STR "2.13.2" VS_VERSION_INFO VERSIONINFO FILEVERSION FT_VERSION diff --git a/src/3rdparty/freetype/src/cff/cffload.c b/src/3rdparty/freetype/src/cff/cffload.c index f96002ec0b8..af79082e98c 100644 --- a/src/3rdparty/freetype/src/cff/cffload.c +++ b/src/3rdparty/freetype/src/cff/cffload.c @@ -1361,14 +1361,15 @@ for ( i = 0; i < numBlends; i++ ) { const FT_Int32* weight = &blend->BV[1]; - FT_UInt32 sum; + FT_Fixed sum; - /* convert inputs to 16.16 fixed-point */ - sum = cff_parse_num( parser, &parser->stack[i + base] ) * 0x10000; + /* convert inputs to 16.16 fixed point */ + sum = cff_parse_fixed( parser, &parser->stack[i + base] ); for ( j = 1; j < blend->lenBV; j++ ) - sum += cff_parse_num( parser, &parser->stack[delta++] ) * *weight++; + sum += FT_MulFix( cff_parse_fixed( parser, &parser->stack[delta++] ), + *weight++ ); /* point parser stack to new value on blend_stack */ parser->stack[i + base] = subFont->blend_top; diff --git a/src/3rdparty/freetype/src/cff/cffparse.c b/src/3rdparty/freetype/src/cff/cffparse.c index c850dfc61e1..3b076704cf7 100644 --- a/src/3rdparty/freetype/src/cff/cffparse.c +++ b/src/3rdparty/freetype/src/cff/cffparse.c @@ -499,6 +499,24 @@ { if ( **d == 30 ) return cff_parse_real( *d, parser->limit, scaling, NULL ); + else if ( **d == 255 ) + { + FT_Fixed val = ( ( ( (FT_UInt32)*( d[0] + 1 ) << 24 ) | + ( (FT_UInt32)*( d[0] + 2 ) << 16 ) | + ( (FT_UInt32)*( d[0] + 3 ) << 8 ) | + (FT_UInt32)*( d[0] + 4 ) ) ); + + if ( scaling ) + { + if ( FT_ABS( val ) > power_ten_limits[scaling] ) + { + FT_TRACE4(( "!!!OVERFLOW:!!!" )); + return val > 0 ? 0x7FFFFFFFL : -0x7FFFFFFFL; + } + val *= power_tens[scaling]; + } + return val; + } else { FT_Long val = cff_parse_integer( *d, parser->limit ); @@ -506,7 +524,7 @@ if ( scaling ) { - if ( FT_ABS( val ) > power_ten_limits[scaling] ) + if ( ( FT_ABS( val ) << 16 ) > power_ten_limits[scaling] ) { val = val > 0 ? 0x7FFFFFFFL : -0x7FFFFFFFL; goto Overflow; @@ -536,7 +554,7 @@ /* read a floating point number, either integer or real */ - static FT_Fixed + FT_LOCAL_DEF( FT_Fixed ) cff_parse_fixed( CFF_Parser parser, FT_Byte** d ) { diff --git a/src/3rdparty/freetype/src/cff/cffparse.h b/src/3rdparty/freetype/src/cff/cffparse.h index b6378a8e8d6..418caacc68c 100644 --- a/src/3rdparty/freetype/src/cff/cffparse.h +++ b/src/3rdparty/freetype/src/cff/cffparse.h @@ -76,6 +76,10 @@ FT_BEGIN_HEADER cff_parse_num( CFF_Parser parser, FT_Byte** d ); + FT_LOCAL( FT_Fixed ) + cff_parse_fixed( CFF_Parser parser, + FT_Byte** d ); + FT_LOCAL( FT_Error ) cff_parser_init( CFF_Parser parser, FT_UInt code, diff --git a/src/3rdparty/freetype/src/gzip/infback.c b/src/3rdparty/freetype/src/gzip/infback.c deleted file mode 100644 index 264c14e0df6..00000000000 --- a/src/3rdparty/freetype/src/gzip/infback.c +++ /dev/null @@ -1,644 +0,0 @@ -/* infback.c -- inflate using a call-back interface - * Copyright (C) 1995-2022 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* - This code is largely copied from inflate.c. Normally either infback.o or - inflate.o would be linked into an application--not both. The interface - with inffast.c is retained so that optimized assembler-coded versions of - inflate_fast() can be used with either inflate.c or infback.c. - */ - -#include "zutil.h" -#include "inftrees.h" -#include "inflate.h" -#include "inffast.h" - -/* function prototypes */ -local void fixedtables OF((struct inflate_state FAR *state)); - -/* - strm provides memory allocation functions in zalloc and zfree, or - Z_NULL to use the library memory allocation functions. - - windowBits is in the range 8..15, and window is a user-supplied - window and output buffer that is 2**windowBits bytes. - */ -int ZEXPORT inflateBackInit_( - z_streamp strm, - int windowBits, - unsigned char FAR *window, - const char *version, - int stream_size) -{ - struct inflate_state FAR *state; - - if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || - stream_size != (int)(sizeof(z_stream))) - return Z_VERSION_ERROR; - if (strm == Z_NULL || window == Z_NULL || - windowBits < 8 || windowBits > 15) - return Z_STREAM_ERROR; - strm->msg = Z_NULL; /* in case we return an error */ - if (strm->zalloc == (alloc_func)0) { -#ifdef Z_SOLO - return Z_STREAM_ERROR; -#else - strm->zalloc = zcalloc; - strm->opaque = (voidpf)0; -#endif - } - if (strm->zfree == (free_func)0) -#ifdef Z_SOLO - return Z_STREAM_ERROR; -#else - strm->zfree = zcfree; -#endif - state = (struct inflate_state FAR *)ZALLOC(strm, 1, - sizeof(struct inflate_state)); - if (state == Z_NULL) return Z_MEM_ERROR; - Tracev((stderr, "inflate: allocated\n")); - strm->state = (struct internal_state FAR *)state; - state->dmax = 32768U; - state->wbits = (uInt)windowBits; - state->wsize = 1U << windowBits; - state->window = window; - state->wnext = 0; - state->whave = 0; - state->sane = 1; - return Z_OK; -} - -/* - Return state with length and distance decoding tables and index sizes set to - fixed code decoding. Normally this returns fixed tables from inffixed.h. - If BUILDFIXED is defined, then instead this routine builds the tables the - first time it's called, and returns those tables the first time and - thereafter. This reduces the size of the code by about 2K bytes, in - exchange for a little execution time. However, BUILDFIXED should not be - used for threaded applications, since the rewriting of the tables and virgin - may not be thread-safe. - */ -local void fixedtables( - struct inflate_state FAR *state) -{ -#ifdef BUILDFIXED - static int virgin = 1; - static code *lenfix, *distfix; - static code fixed[544]; - - /* build fixed huffman tables if first call (may not be thread safe) */ - if (virgin) { - unsigned sym, bits; - static code *next; - - /* literal/length table */ - sym = 0; - while (sym < 144) state->lens[sym++] = 8; - while (sym < 256) state->lens[sym++] = 9; - while (sym < 280) state->lens[sym++] = 7; - while (sym < 288) state->lens[sym++] = 8; - next = fixed; - lenfix = next; - bits = 9; - inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); - - /* distance table */ - sym = 0; - while (sym < 32) state->lens[sym++] = 5; - distfix = next; - bits = 5; - inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); - - /* do this just once */ - virgin = 0; - } -#else /* !BUILDFIXED */ -# include "inffixed.h" -#endif /* BUILDFIXED */ - state->lencode = lenfix; - state->lenbits = 9; - state->distcode = distfix; - state->distbits = 5; -} - -/* Macros for inflateBack(): */ - -/* Load returned state from inflate_fast() */ -#define LOAD() \ - do { \ - put = strm->next_out; \ - left = strm->avail_out; \ - next = strm->next_in; \ - have = strm->avail_in; \ - hold = state->hold; \ - bits = state->bits; \ - } while (0) - -/* Set state from registers for inflate_fast() */ -#define RESTORE() \ - do { \ - strm->next_out = put; \ - strm->avail_out = left; \ - strm->next_in = next; \ - strm->avail_in = have; \ - state->hold = hold; \ - state->bits = bits; \ - } while (0) - -/* Clear the input bit accumulator */ -#define INITBITS() \ - do { \ - hold = 0; \ - bits = 0; \ - } while (0) - -/* Assure that some input is available. If input is requested, but denied, - then return a Z_BUF_ERROR from inflateBack(). */ -#define PULL() \ - do { \ - if (have == 0) { \ - have = in(in_desc, &next); \ - if (have == 0) { \ - next = Z_NULL; \ - ret = Z_BUF_ERROR; \ - goto inf_leave; \ - } \ - } \ - } while (0) - -/* Get a byte of input into the bit accumulator, or return from inflateBack() - with an error if there is no input available. */ -#define PULLBYTE() \ - do { \ - PULL(); \ - have--; \ - hold += (unsigned long)(*next++) << bits; \ - bits += 8; \ - } while (0) - -/* Assure that there are at least n bits in the bit accumulator. If there is - not enough available input to do that, then return from inflateBack() with - an error. */ -#define NEEDBITS(n) \ - do { \ - while (bits < (unsigned)(n)) \ - PULLBYTE(); \ - } while (0) - -/* Return the low n bits of the bit accumulator (n < 16) */ -#define BITS(n) \ - ((unsigned)hold & ((1U << (n)) - 1)) - -/* Remove n bits from the bit accumulator */ -#define DROPBITS(n) \ - do { \ - hold >>= (n); \ - bits -= (unsigned)(n); \ - } while (0) - -/* Remove zero to seven bits as needed to go to a byte boundary */ -#define BYTEBITS() \ - do { \ - hold >>= bits & 7; \ - bits -= bits & 7; \ - } while (0) - -/* Assure that some output space is available, by writing out the window - if it's full. If the write fails, return from inflateBack() with a - Z_BUF_ERROR. */ -#define ROOM() \ - do { \ - if (left == 0) { \ - put = state->window; \ - left = state->wsize; \ - state->whave = left; \ - if (out(out_desc, put, left)) { \ - ret = Z_BUF_ERROR; \ - goto inf_leave; \ - } \ - } \ - } while (0) - -/* - strm provides the memory allocation functions and window buffer on input, - and provides information on the unused input on return. For Z_DATA_ERROR - returns, strm will also provide an error message. - - in() and out() are the call-back input and output functions. When - inflateBack() needs more input, it calls in(). When inflateBack() has - filled the window with output, or when it completes with data in the - window, it calls out() to write out the data. The application must not - change the provided input until in() is called again or inflateBack() - returns. The application must not change the window/output buffer until - inflateBack() returns. - - in() and out() are called with a descriptor parameter provided in the - inflateBack() call. This parameter can be a structure that provides the - information required to do the read or write, as well as accumulated - information on the input and output such as totals and check values. - - in() should return zero on failure. out() should return non-zero on - failure. If either in() or out() fails, than inflateBack() returns a - Z_BUF_ERROR. strm->next_in can be checked for Z_NULL to see whether it - was in() or out() that caused in the error. Otherwise, inflateBack() - returns Z_STREAM_END on success, Z_DATA_ERROR for an deflate format - error, or Z_MEM_ERROR if it could not allocate memory for the state. - inflateBack() can also return Z_STREAM_ERROR if the input parameters - are not correct, i.e. strm is Z_NULL or the state was not initialized. - */ -int ZEXPORT inflateBack( - z_streamp strm, - in_func in, - void FAR *in_desc, - out_func out, - void FAR *out_desc) -{ - struct inflate_state FAR *state; - z_const unsigned char FAR *next; /* next input */ - unsigned char FAR *put; /* next output */ - unsigned have, left; /* available input and output */ - unsigned long hold; /* bit buffer */ - unsigned bits; /* bits in bit buffer */ - unsigned copy; /* number of stored or match bytes to copy */ - unsigned char FAR *from; /* where to copy match bytes from */ - code here; /* current decoding table entry */ - code last; /* parent table entry */ - unsigned len; /* length to copy for repeats, bits to drop */ - int ret; /* return code */ - static const unsigned short order[19] = /* permutation of code lengths */ - {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; - - /* Check that the strm exists and that the state was initialized */ - if (strm == Z_NULL || strm->state == Z_NULL) - return Z_STREAM_ERROR; - state = (struct inflate_state FAR *)strm->state; - - /* Reset the state */ - strm->msg = Z_NULL; - state->mode = TYPE; - state->last = 0; - state->whave = 0; - next = strm->next_in; - have = next != Z_NULL ? strm->avail_in : 0; - hold = 0; - bits = 0; - put = state->window; - left = state->wsize; - - /* Inflate until end of block marked as last */ - for (;;) - switch (state->mode) { - case TYPE: - /* determine and dispatch block type */ - if (state->last) { - BYTEBITS(); - state->mode = DONE; - break; - } - NEEDBITS(3); - state->last = BITS(1); - DROPBITS(1); - switch (BITS(2)) { - case 0: /* stored block */ - Tracev((stderr, "inflate: stored block%s\n", - state->last ? " (last)" : "")); - state->mode = STORED; - break; - case 1: /* fixed block */ - fixedtables(state); - Tracev((stderr, "inflate: fixed codes block%s\n", - state->last ? " (last)" : "")); - state->mode = LEN; /* decode codes */ - break; - case 2: /* dynamic block */ - Tracev((stderr, "inflate: dynamic codes block%s\n", - state->last ? " (last)" : "")); - state->mode = TABLE; - break; - case 3: - strm->msg = (char *)"invalid block type"; - state->mode = BAD; - } - DROPBITS(2); - break; - - case STORED: - /* get and verify stored block length */ - BYTEBITS(); /* go to byte boundary */ - NEEDBITS(32); - if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { - strm->msg = (char *)"invalid stored block lengths"; - state->mode = BAD; - break; - } - state->length = (unsigned)hold & 0xffff; - Tracev((stderr, "inflate: stored length %u\n", - state->length)); - INITBITS(); - - /* copy stored block from input to output */ - while (state->length != 0) { - copy = state->length; - PULL(); - ROOM(); - if (copy > have) copy = have; - if (copy > left) copy = left; - zmemcpy(put, next, copy); - have -= copy; - next += copy; - left -= copy; - put += copy; - state->length -= copy; - } - Tracev((stderr, "inflate: stored end\n")); - state->mode = TYPE; - break; - - case TABLE: - /* get dynamic table entries descriptor */ - NEEDBITS(14); - state->nlen = BITS(5) + 257; - DROPBITS(5); - state->ndist = BITS(5) + 1; - DROPBITS(5); - state->ncode = BITS(4) + 4; - DROPBITS(4); -#ifndef PKZIP_BUG_WORKAROUND - if (state->nlen > 286 || state->ndist > 30) { - strm->msg = (char *)"too many length or distance symbols"; - state->mode = BAD; - break; - } -#endif - Tracev((stderr, "inflate: table sizes ok\n")); - - /* get code length code lengths (not a typo) */ - state->have = 0; - while (state->have < state->ncode) { - NEEDBITS(3); - state->lens[order[state->have++]] = (unsigned short)BITS(3); - DROPBITS(3); - } - while (state->have < 19) - state->lens[order[state->have++]] = 0; - state->next = state->codes; - state->lencode = (code const FAR *)(state->next); - state->lenbits = 7; - ret = inflate_table(CODES, state->lens, 19, &(state->next), - &(state->lenbits), state->work); - if (ret) { - strm->msg = (char *)"invalid code lengths set"; - state->mode = BAD; - break; - } - Tracev((stderr, "inflate: code lengths ok\n")); - - /* get length and distance code code lengths */ - state->have = 0; - while (state->have < state->nlen + state->ndist) { - for (;;) { - here = state->lencode[BITS(state->lenbits)]; - if ((unsigned)(here.bits) <= bits) break; - PULLBYTE(); - } - if (here.val < 16) { - DROPBITS(here.bits); - state->lens[state->have++] = here.val; - } - else { - if (here.val == 16) { - NEEDBITS(here.bits + 2); - DROPBITS(here.bits); - if (state->have == 0) { - strm->msg = (char *)"invalid bit length repeat"; - state->mode = BAD; - break; - } - len = (unsigned)(state->lens[state->have - 1]); - copy = 3 + BITS(2); - DROPBITS(2); - } - else if (here.val == 17) { - NEEDBITS(here.bits + 3); - DROPBITS(here.bits); - len = 0; - copy = 3 + BITS(3); - DROPBITS(3); - } - else { - NEEDBITS(here.bits + 7); - DROPBITS(here.bits); - len = 0; - copy = 11 + BITS(7); - DROPBITS(7); - } - if (state->have + copy > state->nlen + state->ndist) { - strm->msg = (char *)"invalid bit length repeat"; - state->mode = BAD; - break; - } - while (copy--) - state->lens[state->have++] = (unsigned short)len; - } - } - - /* handle error breaks in while */ - if (state->mode == BAD) break; - - /* check for end-of-block code (better have one) */ - if (state->lens[256] == 0) { - strm->msg = (char *)"invalid code -- missing end-of-block"; - state->mode = BAD; - break; - } - - /* build code tables -- note: do not change the lenbits or distbits - values here (9 and 6) without reading the comments in inftrees.h - concerning the ENOUGH constants, which depend on those values */ - state->next = state->codes; - state->lencode = (code const FAR *)(state->next); - state->lenbits = 9; - ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), - &(state->lenbits), state->work); - if (ret) { - strm->msg = (char *)"invalid literal/lengths set"; - state->mode = BAD; - break; - } - state->distcode = (code const FAR *)(state->next); - state->distbits = 6; - ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, - &(state->next), &(state->distbits), state->work); - if (ret) { - strm->msg = (char *)"invalid distances set"; - state->mode = BAD; - break; - } - Tracev((stderr, "inflate: codes ok\n")); - state->mode = LEN; - /* fallthrough */ - - case LEN: - /* use inflate_fast() if we have enough input and output */ - if (have >= 6 && left >= 258) { - RESTORE(); - if (state->whave < state->wsize) - state->whave = state->wsize - left; - inflate_fast(strm, state->wsize); - LOAD(); - break; - } - - /* get a literal, length, or end-of-block code */ - for (;;) { - here = state->lencode[BITS(state->lenbits)]; - if ((unsigned)(here.bits) <= bits) break; - PULLBYTE(); - } - if (here.op && (here.op & 0xf0) == 0) { - last = here; - for (;;) { - here = state->lencode[last.val + - (BITS(last.bits + last.op) >> last.bits)]; - if ((unsigned)(last.bits + here.bits) <= bits) break; - PULLBYTE(); - } - DROPBITS(last.bits); - } - DROPBITS(here.bits); - state->length = (unsigned)here.val; - - /* process literal */ - if (here.op == 0) { - Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? - "inflate: literal '%c'\n" : - "inflate: literal 0x%02x\n", here.val)); - ROOM(); - *put++ = (unsigned char)(state->length); - left--; - state->mode = LEN; - break; - } - - /* process end of block */ - if (here.op & 32) { - Tracevv((stderr, "inflate: end of block\n")); - state->mode = TYPE; - break; - } - - /* invalid code */ - if (here.op & 64) { - strm->msg = (char *)"invalid literal/length code"; - state->mode = BAD; - break; - } - - /* length code -- get extra bits, if any */ - state->extra = (unsigned)(here.op) & 15; - if (state->extra != 0) { - NEEDBITS(state->extra); - state->length += BITS(state->extra); - DROPBITS(state->extra); - } - Tracevv((stderr, "inflate: length %u\n", state->length)); - - /* get distance code */ - for (;;) { - here = state->distcode[BITS(state->distbits)]; - if ((unsigned)(here.bits) <= bits) break; - PULLBYTE(); - } - if ((here.op & 0xf0) == 0) { - last = here; - for (;;) { - here = state->distcode[last.val + - (BITS(last.bits + last.op) >> last.bits)]; - if ((unsigned)(last.bits + here.bits) <= bits) break; - PULLBYTE(); - } - DROPBITS(last.bits); - } - DROPBITS(here.bits); - if (here.op & 64) { - strm->msg = (char *)"invalid distance code"; - state->mode = BAD; - break; - } - state->offset = (unsigned)here.val; - - /* get distance extra bits, if any */ - state->extra = (unsigned)(here.op) & 15; - if (state->extra != 0) { - NEEDBITS(state->extra); - state->offset += BITS(state->extra); - DROPBITS(state->extra); - } - if (state->offset > state->wsize - (state->whave < state->wsize ? - left : 0)) { - strm->msg = (char *)"invalid distance too far back"; - state->mode = BAD; - break; - } - Tracevv((stderr, "inflate: distance %u\n", state->offset)); - - /* copy match from window to output */ - do { - ROOM(); - copy = state->wsize - state->offset; - if (copy < left) { - from = put + copy; - copy = left - copy; - } - else { - from = put - state->offset; - copy = left; - } - if (copy > state->length) copy = state->length; - state->length -= copy; - left -= copy; - do { - *put++ = *from++; - } while (--copy); - } while (state->length != 0); - break; - - case DONE: - /* inflate stream terminated properly */ - ret = Z_STREAM_END; - goto inf_leave; - - case BAD: - ret = Z_DATA_ERROR; - goto inf_leave; - - default: - /* can't happen, but makes compilers happy */ - ret = Z_STREAM_ERROR; - goto inf_leave; - } - - /* Write leftover output and return unused input */ - inf_leave: - if (left < state->wsize) { - if (out(out_desc, state->window, state->wsize - left) && - ret == Z_STREAM_END) - ret = Z_BUF_ERROR; - } - strm->next_in = next; - strm->avail_in = have; - return ret; -} - -int ZEXPORT inflateBackEnd( - z_streamp strm) -{ - if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) - return Z_STREAM_ERROR; - ZFREE(strm, strm->state); - strm->state = Z_NULL; - Tracev((stderr, "inflate: end\n")); - return Z_OK; -} diff --git a/src/3rdparty/freetype/src/gzip/rules.mk b/src/3rdparty/freetype/src/gzip/rules.mk index 050e14705c0..c76eacb1ae7 100644 --- a/src/3rdparty/freetype/src/gzip/rules.mk +++ b/src/3rdparty/freetype/src/gzip/rules.mk @@ -36,24 +36,23 @@ endif # # All source and header files get loaded by `ftgzip.c' only if SYSTEM_ZLIB # is not defined (regardless whether we have a `single' or a `multi' build). -# However, it doesn't harm if we add everything as a dependency -# unconditionally. # -GZIP_DRV_SRCS := $(GZIP_DIR)/adler32.c \ - $(GZIP_DIR)/crc32.c \ - $(GZIP_DIR)/crc32.h \ - $(GZIP_DIR)/ftzconf.h \ - $(GZIP_DIR)/inffast.c \ - $(GZIP_DIR)/inffast.h \ - $(GZIP_DIR)/inffixed.h \ - $(GZIP_DIR)/inflate.c \ - $(GZIP_DIR)/inflate.h \ - $(GZIP_DIR)/inftrees.c \ - $(GZIP_DIR)/inftrees.h \ - $(GZIP_DIR)/zlib.h \ - $(GZIP_DIR)/zutil.c \ - $(GZIP_DIR)/zutil.h - +ifeq ($(SYSTEM_ZLIB),) + GZIP_DRV_SRCS := $(GZIP_DIR)/adler32.c \ + $(GZIP_DIR)/crc32.c \ + $(GZIP_DIR)/crc32.h \ + $(GZIP_DIR)/ftzconf.h \ + $(GZIP_DIR)/inffast.c \ + $(GZIP_DIR)/inffast.h \ + $(GZIP_DIR)/inffixed.h \ + $(GZIP_DIR)/inflate.c \ + $(GZIP_DIR)/inflate.h \ + $(GZIP_DIR)/inftrees.c \ + $(GZIP_DIR)/inftrees.h \ + $(GZIP_DIR)/zlib.h \ + $(GZIP_DIR)/zutil.c \ + $(GZIP_DIR)/zutil.h +endif # gzip driver object(s) # diff --git a/src/3rdparty/freetype/src/psaux/cffdecode.c b/src/3rdparty/freetype/src/psaux/cffdecode.c index 2cd91c96f35..562d17d2216 100644 --- a/src/3rdparty/freetype/src/psaux/cffdecode.c +++ b/src/3rdparty/freetype/src/psaux/cffdecode.c @@ -2153,7 +2153,7 @@ decoder->locals_bias ); - FT_TRACE4(( " callsubr (idx %d, entering level %ld)\n", + FT_TRACE4(( " callsubr (idx %d, entering level %td)\n", idx, zone - decoder->zones + 1 )); @@ -2197,7 +2197,7 @@ decoder->globals_bias ); - FT_TRACE4(( " callgsubr (idx %d, entering level %ld)\n", + FT_TRACE4(( " callgsubr (idx %d, entering level %td)\n", idx, zone - decoder->zones + 1 )); @@ -2236,7 +2236,7 @@ break; case cff_op_return: - FT_TRACE4(( " return (leaving level %ld)\n", + FT_TRACE4(( " return (leaving level %td)\n", decoder->zone - decoder->zones )); if ( decoder->zone <= decoder->zones ) diff --git a/src/3rdparty/freetype/src/psaux/pshints.c b/src/3rdparty/freetype/src/psaux/pshints.c index 6f44d0adbb7..7bd08a9c9bf 100644 --- a/src/3rdparty/freetype/src/psaux/pshints.c +++ b/src/3rdparty/freetype/src/psaux/pshints.c @@ -310,7 +310,7 @@ CF2_Hint hint = &hintmap->edge[i]; - FT_TRACE6(( " %3ld %7.2f %7.2f %5d %s%s%s%s\n", + FT_TRACE6(( " %3zu %7.2f %7.2f %5d %s%s%s%s\n", hint->index, hint->csCoord / 65536.0, hint->dsCoord / ( hint->scale * 1.0 ), diff --git a/src/3rdparty/freetype/src/psaux/t1decode.c b/src/3rdparty/freetype/src/psaux/t1decode.c index bfed45b53a3..4b6b969bcb9 100644 --- a/src/3rdparty/freetype/src/psaux/t1decode.c +++ b/src/3rdparty/freetype/src/psaux/t1decode.c @@ -520,7 +520,7 @@ #ifdef FT_DEBUG_LEVEL_TRACE if ( bol ) { - FT_TRACE5(( " (%ld)", decoder->top - decoder->stack )); + FT_TRACE5(( " (%td)", decoder->top - decoder->stack )); bol = FALSE; } #endif @@ -1165,7 +1165,7 @@ if ( top - decoder->stack != num_args ) FT_TRACE0(( "t1_decoder_parse_charstrings:" " too much operands on the stack" - " (seen %ld, expected %d)\n", + " (seen %td, expected %d)\n", top - decoder->stack, num_args )); break; } diff --git a/src/3rdparty/freetype/src/sfnt/sfwoff2.c b/src/3rdparty/freetype/src/sfnt/sfwoff2.c index 49ad5cc8114..2be44a347ad 100644 --- a/src/3rdparty/freetype/src/sfnt/sfwoff2.c +++ b/src/3rdparty/freetype/src/sfnt/sfwoff2.c @@ -36,6 +36,8 @@ #undef FT_COMPONENT #define FT_COMPONENT sfwoff2 + /* An arbitrary, heuristic size limit (67MByte) for expanded WOFF2 data. */ +#define MAX_SFNT_SIZE ( 1 << 26 ) #define READ_255USHORT( var ) FT_SET_ERROR( Read255UShort( stream, &var ) ) @@ -2180,9 +2182,8 @@ else sfnt_size = woff2.totalSfntSize; - /* Value 1<<26 = 67108864 is heuristic. */ - if (sfnt_size >= (1 << 26)) - sfnt_size = 1 << 26; + if ( sfnt_size >= MAX_SFNT_SIZE ) + sfnt_size = MAX_SFNT_SIZE; #ifdef FT_DEBUG_LEVEL_TRACE if ( sfnt_size != woff2.totalSfntSize ) @@ -2257,10 +2258,15 @@ goto Exit; } - if ( woff2.uncompressed_size > sfnt_size ) + /* We must not blindly trust `uncompressed_size` since its */ + /* value might be corrupted. If it is too large, reject the */ + /* font. In other words, we don't accept a WOFF2 font that */ + /* expands to something larger than MAX_SFNT_SIZE. If ever */ + /* necessary, this limit can be easily adjusted. */ + if ( woff2.uncompressed_size > MAX_SFNT_SIZE ) { - FT_ERROR(( "woff2_open_font: SFNT table lengths are too large.\n" )); - error = FT_THROW( Invalid_Table ); + FT_ERROR(( "Uncompressed font too large.\n" )); + error = FT_THROW( Array_Too_Large ); goto Exit; } diff --git a/src/3rdparty/freetype/src/sfnt/ttcolr.c b/src/3rdparty/freetype/src/sfnt/ttcolr.c index 69ccf0ee7ae..281e7135eea 100644 --- a/src/3rdparty/freetype/src/sfnt/ttcolr.c +++ b/src/3rdparty/freetype/src/sfnt/ttcolr.c @@ -229,7 +229,7 @@ base_glyphs_offset_v1 = FT_NEXT_ULONG( p ); - if ( base_glyphs_offset_v1 + 4 >= table_size ) + if ( base_glyphs_offset_v1 >= table_size - 4 ) goto InvalidTable; p1 = (FT_Byte*)( table + base_glyphs_offset_v1 ); @@ -249,7 +249,7 @@ if ( layer_offset_v1 ) { - if ( layer_offset_v1 + 4 >= table_size ) + if ( layer_offset_v1 >= table_size - 4 ) goto InvalidTable; p1 = (FT_Byte*)( table + layer_offset_v1 ); diff --git a/src/3rdparty/freetype/src/smooth/ftgrays.c b/src/3rdparty/freetype/src/smooth/ftgrays.c index 6252df98ae6..0918272f870 100644 --- a/src/3rdparty/freetype/src/smooth/ftgrays.c +++ b/src/3rdparty/freetype/src/smooth/ftgrays.c @@ -1006,10 +1006,11 @@ typedef ptrdiff_t FT_PtrDist; * * For other cases, using binary splits is actually slightly faster. */ -#if defined( __SSE2__ ) || \ - ( defined( __x86_64__ ) && !defined( __VMS ) ) || \ - defined( _M_AMD64 ) || \ - ( defined( _M_IX86_FP ) && _M_IX86_FP >= 2 ) +#if ( defined( __SSE2__ ) || \ + defined( __x86_64__ ) || \ + defined( _M_AMD64 ) || \ + ( defined( _M_IX86_FP ) && _M_IX86_FP >= 2 ) ) && \ + !defined( __VMS ) # define FT_SSE2 1 #else # define FT_SSE2 0 @@ -1933,7 +1934,7 @@ typedef ptrdiff_t FT_PtrDist; if ( continued ) FT_Trace_Enable(); - FT_TRACE7(( "band [%d..%d]: %ld cell%s remaining/\n", + FT_TRACE7(( "band [%d..%d]: %td cell%s remaining/\n", ras.min_ey, ras.max_ey, ras.cell_null - ras.cell_free, diff --git a/src/3rdparty/freetype/src/truetype/rules.mk b/src/3rdparty/freetype/src/truetype/rules.mk index 23f6f006ddd..dde26de1cc2 100644 --- a/src/3rdparty/freetype/src/truetype/rules.mk +++ b/src/3rdparty/freetype/src/truetype/rules.mk @@ -33,8 +33,7 @@ TT_DRV_SRC := $(TT_DIR)/ttdriver.c \ $(TT_DIR)/ttgxvar.c \ $(TT_DIR)/ttinterp.c \ $(TT_DIR)/ttobjs.c \ - $(TT_DIR)/ttpload.c \ - $(TT_DIR)/ttsubpix.c + $(TT_DIR)/ttpload.c # TrueType driver headers # diff --git a/src/3rdparty/freetype/src/truetype/truetype.c b/src/3rdparty/freetype/src/truetype/truetype.c index c5faa96270b..fcc0ea334ff 100644 --- a/src/3rdparty/freetype/src/truetype/truetype.c +++ b/src/3rdparty/freetype/src/truetype/truetype.c @@ -24,7 +24,6 @@ #include "ttinterp.c" #include "ttobjs.c" /* object manager */ #include "ttpload.c" /* tables loader */ -#include "ttsubpix.c" /* END */ diff --git a/src/3rdparty/freetype/src/truetype/ttdriver.c b/src/3rdparty/freetype/src/truetype/ttdriver.c index 7151e820739..d1496fec7fa 100644 --- a/src/3rdparty/freetype/src/truetype/ttdriver.c +++ b/src/3rdparty/freetype/src/truetype/ttdriver.c @@ -100,11 +100,6 @@ break; case TT_INTERPRETER_VERSION_38: -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - driver->interpreter_version = TT_INTERPRETER_VERSION_38; - break; -#endif - case TT_INTERPRETER_VERSION_40: #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL driver->interpreter_version = TT_INTERPRETER_VERSION_40; diff --git a/src/3rdparty/freetype/src/truetype/ttgload.c b/src/3rdparty/freetype/src/truetype/ttgload.c index 5f15a7f4def..dc427e8a116 100644 --- a/src/3rdparty/freetype/src/truetype/ttgload.c +++ b/src/3rdparty/freetype/src/truetype/ttgload.c @@ -35,7 +35,6 @@ #endif #include "tterrors.h" -#include "ttsubpix.h" /************************************************************************** @@ -152,9 +151,6 @@ FT_UInt glyph_index ) { TT_Face face = loader->face; -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - TT_Driver driver = (TT_Driver)FT_FACE_DRIVER( face ); -#endif FT_Error error; FT_Stream stream = loader->stream; @@ -183,20 +179,6 @@ loader->top_bearing = top_bearing; loader->vadvance = advance_height; -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - if ( driver->interpreter_version == TT_INTERPRETER_VERSION_38 && - loader->exec ) - { - loader->exec->sph_tweak_flags = 0; - - /* This may not be the right place for this, but it works... */ - /* Note that we have to unconditionally load the tweaks since */ - /* it is possible that glyphs individually switch ClearType's */ - /* backward compatibility mode on and off. */ - sph_set_tweaks( loader, glyph_index ); - } -#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ - #ifdef FT_CONFIG_OPTION_INCREMENTAL /* With the incremental interface, these values are set by */ /* a call to `tt_get_metrics_incremental'. */ @@ -798,8 +780,7 @@ TT_Hint_Glyph( TT_Loader loader, FT_Bool is_composite ) { -#if defined TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY || \ - defined TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL +#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL TT_Face face = loader->face; TT_Driver driver = (TT_Driver)FT_FACE_DRIVER( face ); #endif @@ -820,7 +801,7 @@ FT_ARRAY_COPY( zone->org, zone->cur, zone->n_points ); /* Reset graphics state. */ - loader->exec->GS = loader->size->GS; + exec->GS = loader->size->GS; /* XXX: UNDOCUMENTED! Hinting instructions of a composite glyph */ /* completely refer to the (already) hinted subglyphs. */ @@ -860,7 +841,7 @@ exec->is_composite = is_composite; exec->pts = *zone; - error = TT_Run_Context( loader->exec ); + error = TT_Run_Context( exec ); if ( error && exec->pedantic_hinting ) return error; @@ -871,32 +852,20 @@ #endif -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL /* Save possibly modified glyph phantom points unless in v40 backward */ /* compatibility mode, where no movement on the x axis means no reason */ /* to change bearings or advance widths. */ - if ( !( driver->interpreter_version == TT_INTERPRETER_VERSION_40 && - exec->backward_compatibility ) ) - { -#endif - loader->pp1 = zone->cur[zone->n_points - 4]; - loader->pp2 = zone->cur[zone->n_points - 3]; - loader->pp3 = zone->cur[zone->n_points - 2]; - loader->pp4 = zone->cur[zone->n_points - 1]; + #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL - } + if ( driver->interpreter_version == TT_INTERPRETER_VERSION_40 && + exec->backward_compatibility ) + return FT_Err_Ok; #endif -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - if ( driver->interpreter_version == TT_INTERPRETER_VERSION_38 ) - { - if ( exec->sph_tweak_flags & SPH_TWEAK_DEEMBOLDEN ) - FT_Outline_EmboldenXY( &loader->gloader->current.outline, -24, 0 ); - - else if ( exec->sph_tweak_flags & SPH_TWEAK_EMBOLDEN ) - FT_Outline_EmboldenXY( &loader->gloader->current.outline, 24, 0 ); - } -#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ + loader->pp1 = zone->cur[zone->n_points - 4]; + loader->pp2 = zone->cur[zone->n_points - 3]; + loader->pp3 = zone->cur[zone->n_points - 2]; + loader->pp4 = zone->cur[zone->n_points - 1]; return FT_Err_Ok; } @@ -960,16 +929,6 @@ } { -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - TT_Face face = loader->face; - TT_Driver driver = (TT_Driver)FT_FACE_DRIVER( face ); - - FT_String* family = face->root.family_name; - FT_UInt ppem = loader->size->metrics->x_ppem; - FT_String* style = face->root.style_name; - FT_UInt x_scale_factor = 1000; -#endif - FT_Vector* vec = outline->points; FT_Vector* limit = outline->points + n_points; @@ -979,52 +938,6 @@ FT_Bool do_scale = FALSE; -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - - if ( driver->interpreter_version == TT_INTERPRETER_VERSION_38 ) - { - /* scale, but only if enabled and only if TT hinting is being used */ - if ( IS_HINTED( loader->load_flags ) ) - x_scale_factor = sph_test_tweak_x_scaling( face, - family, - ppem, - style, - loader->glyph_index ); - /* scale the glyph */ - if ( ( loader->load_flags & FT_LOAD_NO_SCALE ) == 0 || - x_scale_factor != 1000 ) - { - x_scale = FT_MulDiv( loader->size->metrics->x_scale, - (FT_Long)x_scale_factor, 1000 ); - y_scale = loader->size->metrics->y_scale; - - /* compensate for any scaling by de/emboldening; */ - /* the amount was determined via experimentation */ - if ( x_scale_factor != 1000 && ppem > 11 ) - { -#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT - FT_Vector* orig_points = outline->points; - - - if ( !IS_DEFAULT_INSTANCE( FT_FACE( loader->face ) ) ) - outline->points = unrounded; -#endif - FT_Outline_EmboldenXY( outline, - FT_MulFix( 1280 * ppem, - 1000 - x_scale_factor ), - 0 ); -#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT - if ( !IS_DEFAULT_INSTANCE( FT_FACE( loader->face ) ) ) - outline->points = orig_points; -#endif - } - do_scale = TRUE; - } - } - else - -#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ - { /* scale the glyph */ if ( ( loader->load_flags & FT_LOAD_NO_SCALE ) == 0 ) @@ -1451,45 +1364,31 @@ static void tt_loader_set_pp( TT_Loader loader ) { - FT_Bool subpixel_hinting = 0; - FT_Bool grayscale = 0; - FT_Bool use_aw_2 = 0; - -#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING - TT_Driver driver = (TT_Driver)FT_FACE_DRIVER( loader->face ); -#endif - - -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - if ( driver->interpreter_version == TT_INTERPRETER_VERSION_38 ) - { - subpixel_hinting = loader->exec ? loader->exec->subpixel_hinting - : 0; - grayscale = loader->exec ? loader->exec->grayscale - : 0; - } -#endif -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL - if ( driver->interpreter_version == TT_INTERPRETER_VERSION_40 ) - { - subpixel_hinting = loader->exec ? loader->exec->subpixel_hinting_lean - : 0; - grayscale = loader->exec ? loader->exec->grayscale_cleartype - : 0; - } -#endif - - use_aw_2 = FT_BOOL( subpixel_hinting && grayscale ); - loader->pp1.x = loader->bbox.xMin - loader->left_bearing; loader->pp1.y = 0; loader->pp2.x = loader->pp1.x + loader->advance; loader->pp2.y = 0; - loader->pp3.x = use_aw_2 ? loader->advance / 2 : 0; + loader->pp3.x = 0; loader->pp3.y = loader->bbox.yMax + loader->top_bearing; - loader->pp4.x = use_aw_2 ? loader->advance / 2 : 0; + loader->pp4.x = 0; loader->pp4.y = loader->pp3.y - loader->vadvance; + +#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL + { + TT_Driver driver = (TT_Driver)FT_FACE_DRIVER( loader->face ); + + + if ( driver->interpreter_version == TT_INTERPRETER_VERSION_40 && + loader->exec && + loader->exec->subpixel_hinting_lean && + loader->exec->grayscale_cleartype ) + { + loader->pp3.x = loader->advance / 2; + loader->pp4.x = loader->advance / 2; + } + } +#endif } @@ -2277,8 +2176,7 @@ #ifdef TT_USE_BYTECODE_INTERPRETER FT_Error error; FT_Bool pedantic = FT_BOOL( load_flags & FT_LOAD_PEDANTIC ); -#if defined TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY || \ - defined TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL +#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL TT_Driver driver = (TT_Driver)FT_FACE_DRIVER( glyph->face ); #endif #endif @@ -2298,20 +2196,6 @@ FT_Bool grayscale_cleartype; #endif -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - FT_Bool subpixel_hinting = FALSE; - -#if 0 - /* not used yet */ - FT_Bool compatible_widths; - FT_Bool symmetrical_smoothing; - FT_Bool bgr; - FT_Bool vertical_lcd; - FT_Bool subpixel_positioned; - FT_Bool gray_cleartype; -#endif -#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ - FT_Bool reexecute = FALSE; @@ -2331,6 +2215,9 @@ if ( !exec ) return FT_THROW( Could_Not_Find_Context ); + grayscale = FT_BOOL( FT_LOAD_TARGET_MODE( load_flags ) != + FT_RENDER_MODE_MONO ); + #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL if ( driver->interpreter_version == TT_INTERPRETER_VERSION_40 ) { @@ -2347,6 +2234,7 @@ FT_BOOL( subpixel_hinting_lean && ( load_flags & FT_LOAD_TARGET_LCD_V ) ); + grayscale = FT_BOOL( grayscale && !subpixel_hinting_lean ); } else { @@ -2356,111 +2244,11 @@ } #endif -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - - if ( driver->interpreter_version == TT_INTERPRETER_VERSION_38 ) - { - subpixel_hinting = FT_BOOL( ( FT_LOAD_TARGET_MODE( load_flags ) != - FT_RENDER_MODE_MONO ) && - SPH_OPTION_SET_SUBPIXEL ); - - if ( subpixel_hinting ) - grayscale = FALSE; - else if ( SPH_OPTION_SET_GRAYSCALE ) - { - grayscale = TRUE; - subpixel_hinting = FALSE; - } - else - grayscale = FALSE; - - if ( FT_IS_TRICKY( glyph->face ) ) - subpixel_hinting = FALSE; - - exec->ignore_x_mode = subpixel_hinting || grayscale; - exec->rasterizer_version = SPH_OPTION_SET_RASTERIZER_VERSION; - if ( exec->sph_tweak_flags & SPH_TWEAK_RASTERIZER_35 ) - exec->rasterizer_version = TT_INTERPRETER_VERSION_35; - -#if 1 - exec->compatible_widths = SPH_OPTION_SET_COMPATIBLE_WIDTHS; - exec->symmetrical_smoothing = TRUE; - exec->bgr = FALSE; - exec->vertical_lcd = FALSE; - exec->subpixel_positioned = TRUE; - exec->gray_cleartype = FALSE; -#else /* 0 */ - exec->compatible_widths = - FT_BOOL( FT_LOAD_TARGET_MODE( load_flags ) != - TT_LOAD_COMPATIBLE_WIDTHS ); - exec->symmetrical_smoothing = - FT_BOOL( FT_LOAD_TARGET_MODE( load_flags ) != - TT_LOAD_SYMMETRICAL_SMOOTHING ); - exec->bgr = - FT_BOOL( FT_LOAD_TARGET_MODE( load_flags ) != - TT_LOAD_BGR ); - exec->vertical_lcd = - FT_BOOL( FT_LOAD_TARGET_MODE( load_flags ) != - TT_LOAD_VERTICAL_LCD ); - exec->subpixel_positioned = - FT_BOOL( FT_LOAD_TARGET_MODE( load_flags ) != - TT_LOAD_SUBPIXEL_POSITIONED ); - exec->gray_cleartype = - FT_BOOL( FT_LOAD_TARGET_MODE( load_flags ) != - TT_LOAD_GRAY_CLEARTYPE ); -#endif /* 0 */ - - } - else - -#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ - -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL - if ( driver->interpreter_version == TT_INTERPRETER_VERSION_40 ) - grayscale = FT_BOOL( !subpixel_hinting_lean && - FT_LOAD_TARGET_MODE( load_flags ) != - FT_RENDER_MODE_MONO ); - else -#endif - grayscale = FT_BOOL( FT_LOAD_TARGET_MODE( load_flags ) != - FT_RENDER_MODE_MONO ); - error = TT_Load_Context( exec, face, size ); if ( error ) return error; -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - - if ( driver->interpreter_version == TT_INTERPRETER_VERSION_38 ) { - /* a change from mono to subpixel rendering (and vice versa) */ - /* requires a re-execution of the CVT program */ - if ( subpixel_hinting != exec->subpixel_hinting ) - { - FT_TRACE4(( "tt_loader_init: subpixel hinting change," - " re-executing `prep' table\n" )); - - exec->subpixel_hinting = subpixel_hinting; - reexecute = TRUE; - } - - /* a change from mono to grayscale rendering (and vice versa) */ - /* requires a re-execution of the CVT program */ - if ( grayscale != exec->grayscale ) - { - FT_TRACE4(( "tt_loader_init: grayscale hinting change," - " re-executing `prep' table\n" )); - - exec->grayscale = grayscale; - reexecute = TRUE; - } - } - else - -#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ - - { - #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL if ( driver->interpreter_version == TT_INTERPRETER_VERSION_40 ) { @@ -2518,14 +2306,6 @@ if ( exec->GS.instruct_control & 2 ) exec->GS = tt_default_graphics_state; -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - /* check whether we have a font hinted for ClearType -- */ - /* note that this flag can also be modified in a glyph's bytecode */ - if ( driver->interpreter_version == TT_INTERPRETER_VERSION_38 && - exec->GS.instruct_control & 4 ) - exec->ignore_x_mode = FALSE; -#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ - #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL /* * Toggle backward compatibility according to what font wants, except @@ -2561,13 +2341,6 @@ #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL !( driver->interpreter_version == TT_INTERPRETER_VERSION_40 && exec->backward_compatibility ) && -#endif -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - !( driver->interpreter_version == TT_INTERPRETER_VERSION_38 && - !SPH_OPTION_BITMAP_WIDTHS && - FT_LOAD_TARGET_MODE( loader->load_flags ) != - FT_RENDER_MODE_MONO && - exec->compatible_widths ) && #endif !face->postscript.isFixedPitch ) { diff --git a/src/3rdparty/freetype/src/truetype/ttgxvar.c b/src/3rdparty/freetype/src/truetype/ttgxvar.c index 8c713f1b6fe..ad4f266b27a 100644 --- a/src/3rdparty/freetype/src/truetype/ttgxvar.c +++ b/src/3rdparty/freetype/src/truetype/ttgxvar.c @@ -621,10 +621,10 @@ { GX_ItemVarData varData = &itemStore->varData[i]; - FT_UInt item_count; - FT_UInt word_delta_count; - FT_UInt region_idx_count; - FT_UInt per_region_size; + FT_UInt item_count; + FT_UShort word_delta_count; + FT_UInt region_idx_count; + FT_UInt per_region_size; if ( FT_STREAM_SEEK( offset + dataOffsetArray[i] ) ) diff --git a/src/3rdparty/freetype/src/truetype/ttinterp.c b/src/3rdparty/freetype/src/truetype/ttinterp.c index 34c3e6c92a4..79df4555d94 100644 --- a/src/3rdparty/freetype/src/truetype/ttinterp.c +++ b/src/3rdparty/freetype/src/truetype/ttinterp.c @@ -29,7 +29,6 @@ #include "ttinterp.h" #include "tterrors.h" -#include "ttsubpix.h" #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT #include "ttgxvar.h" #endif @@ -52,12 +51,6 @@ ( ((TT_Driver)FT_FACE_DRIVER( exc->face ))->interpreter_version == \ TT_INTERPRETER_VERSION_35 ) -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY -#define SUBPIXEL_HINTING_INFINALITY \ - ( ((TT_Driver)FT_FACE_DRIVER( exc->face ))->interpreter_version == \ - TT_INTERPRETER_VERSION_38 ) -#endif - #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL #define SUBPIXEL_HINTING_MINIMAL \ ( ((TT_Driver)FT_FACE_DRIVER( exc->face ))->interpreter_version == \ @@ -1685,17 +1678,6 @@ if ( v != 0 ) { -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - if ( SUBPIXEL_HINTING_INFINALITY && - ( !exc->ignore_x_mode || - ( exc->sph_tweak_flags & SPH_TWEAK_ALLOW_X_DMOVE ) ) ) - zone->cur[point].x = ADD_LONG( zone->cur[point].x, - FT_MulDiv( distance, - v, - exc->F_dot_P ) ); - else -#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ - #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL /* Exception to the post-IUP curfew: Allow the x component of */ /* diagonal moves, but only post-IUP. DejaVu tries to adjust */ @@ -1801,12 +1783,6 @@ FT_UShort point, FT_F26Dot6 distance ) { -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - if ( SUBPIXEL_HINTING_INFINALITY && !exc->ignore_x_mode ) - zone->cur[point].x = ADD_LONG( zone->cur[point].x, distance ); - else -#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ - #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL if ( SUBPIXEL_HINTING_MINIMAL && !exc->backward_compatibility ) zone->cur[point].x = ADD_LONG( zone->cur[point].x, distance ); @@ -3010,28 +2986,7 @@ args[0] = 0; } else - { -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - /* subpixel hinting - avoid Typeman Dstroke and */ - /* IStroke and Vacuform rounds */ - if ( SUBPIXEL_HINTING_INFINALITY && - exc->ignore_x_mode && - ( ( I == 24 && - ( exc->face->sph_found_func_flags & - ( SPH_FDEF_SPACING_1 | - SPH_FDEF_SPACING_2 ) ) ) || - ( I == 22 && - ( exc->sph_in_func_flags & - SPH_FDEF_TYPEMAN_STROKES ) ) || - ( I == 8 && - ( exc->face->sph_found_func_flags & - SPH_FDEF_VACUFORM_ROUND_1 ) && - exc->iup_called ) ) ) - args[0] = 0; - else -#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ - args[0] = exc->storage[I]; - } + args[0] = exc->storage[I]; } @@ -3545,107 +3500,6 @@ TT_DefRecord* rec; TT_DefRecord* limit; -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - /* arguments to opcodes are skipped by `SKIP_Code' */ - FT_Byte opcode_pattern[9][12] = - { - /* #0 inline delta function 1 */ - { - 0x4B, /* PPEM */ - 0x53, /* GTEQ */ - 0x23, /* SWAP */ - 0x4B, /* PPEM */ - 0x51, /* LTEQ */ - 0x5A, /* AND */ - 0x58, /* IF */ - 0x38, /* SHPIX */ - 0x1B, /* ELSE */ - 0x21, /* POP */ - 0x21, /* POP */ - 0x59 /* EIF */ - }, - /* #1 inline delta function 2 */ - { - 0x4B, /* PPEM */ - 0x54, /* EQ */ - 0x58, /* IF */ - 0x38, /* SHPIX */ - 0x1B, /* ELSE */ - 0x21, /* POP */ - 0x21, /* POP */ - 0x59 /* EIF */ - }, - /* #2 diagonal stroke function */ - { - 0x20, /* DUP */ - 0x20, /* DUP */ - 0xB0, /* PUSHB_1 */ - /* 1 */ - 0x60, /* ADD */ - 0x46, /* GC_cur */ - 0xB0, /* PUSHB_1 */ - /* 64 */ - 0x23, /* SWAP */ - 0x42 /* WS */ - }, - /* #3 VacuFormRound function */ - { - 0x45, /* RCVT */ - 0x23, /* SWAP */ - 0x46, /* GC_cur */ - 0x60, /* ADD */ - 0x20, /* DUP */ - 0xB0 /* PUSHB_1 */ - /* 38 */ - }, - /* #4 TTFautohint bytecode (old) */ - { - 0x20, /* DUP */ - 0x64, /* ABS */ - 0xB0, /* PUSHB_1 */ - /* 32 */ - 0x60, /* ADD */ - 0x66, /* FLOOR */ - 0x23, /* SWAP */ - 0xB0 /* PUSHB_1 */ - }, - /* #5 spacing function 1 */ - { - 0x01, /* SVTCA_x */ - 0xB0, /* PUSHB_1 */ - /* 24 */ - 0x43, /* RS */ - 0x58 /* IF */ - }, - /* #6 spacing function 2 */ - { - 0x01, /* SVTCA_x */ - 0x18, /* RTG */ - 0xB0, /* PUSHB_1 */ - /* 24 */ - 0x43, /* RS */ - 0x58 /* IF */ - }, - /* #7 TypeMan Talk DiagEndCtrl function */ - { - 0x01, /* SVTCA_x */ - 0x20, /* DUP */ - 0xB0, /* PUSHB_1 */ - /* 3 */ - 0x25, /* CINDEX */ - }, - /* #8 TypeMan Talk Align */ - { - 0x06, /* SPVTL */ - 0x7D, /* RDTG */ - }, - }; - FT_UShort opcode_patterns = 9; - FT_UShort opcode_pointer[9] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - FT_UShort opcode_size[9] = { 12, 8, 8, 6, 7, 4, 5, 4, 2 }; - FT_UShort i; -#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ - /* FDEF is only allowed in `prep' or `fpgm' */ if ( exc->iniRange == tt_coderange_glyph ) @@ -3690,136 +3544,15 @@ rec->opc = (FT_UInt16)n; rec->start = exc->IP + 1; rec->active = TRUE; - rec->inline_delta = FALSE; - rec->sph_fdef_flags = 0x0000; if ( n > exc->maxFunc ) exc->maxFunc = (FT_UInt16)n; -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - /* We don't know for sure these are typeman functions, */ - /* however they are only active when RS 22 is called */ - if ( n >= 64 && n <= 66 ) - rec->sph_fdef_flags |= SPH_FDEF_TYPEMAN_STROKES; -#endif - /* Now skip the whole function definition. */ /* We don't allow nested IDEFS & FDEFs. */ while ( SkipCode( exc ) == SUCCESS ) { - -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - - if ( SUBPIXEL_HINTING_INFINALITY ) - { - for ( i = 0; i < opcode_patterns; i++ ) - { - if ( opcode_pointer[i] < opcode_size[i] && - exc->opcode == opcode_pattern[i][opcode_pointer[i]] ) - { - opcode_pointer[i] += 1; - - if ( opcode_pointer[i] == opcode_size[i] ) - { - FT_TRACE6(( "sph: Function %d, opcode ptrn: %ld, %s %s\n", - i, n, - exc->face->root.family_name, - exc->face->root.style_name )); - - switch ( i ) - { - case 0: - rec->sph_fdef_flags |= SPH_FDEF_INLINE_DELTA_1; - exc->face->sph_found_func_flags |= SPH_FDEF_INLINE_DELTA_1; - break; - - case 1: - rec->sph_fdef_flags |= SPH_FDEF_INLINE_DELTA_2; - exc->face->sph_found_func_flags |= SPH_FDEF_INLINE_DELTA_2; - break; - - case 2: - switch ( n ) - { - /* needs to be implemented still */ - case 58: - rec->sph_fdef_flags |= SPH_FDEF_DIAGONAL_STROKE; - exc->face->sph_found_func_flags |= SPH_FDEF_DIAGONAL_STROKE; - } - break; - - case 3: - switch ( n ) - { - case 0: - rec->sph_fdef_flags |= SPH_FDEF_VACUFORM_ROUND_1; - exc->face->sph_found_func_flags |= SPH_FDEF_VACUFORM_ROUND_1; - } - break; - - case 4: - /* probably not necessary to detect anymore */ - rec->sph_fdef_flags |= SPH_FDEF_TTFAUTOHINT_1; - exc->face->sph_found_func_flags |= SPH_FDEF_TTFAUTOHINT_1; - break; - - case 5: - switch ( n ) - { - case 0: - case 1: - case 2: - case 4: - case 7: - case 8: - rec->sph_fdef_flags |= SPH_FDEF_SPACING_1; - exc->face->sph_found_func_flags |= SPH_FDEF_SPACING_1; - } - break; - - case 6: - switch ( n ) - { - case 0: - case 1: - case 2: - case 4: - case 7: - case 8: - rec->sph_fdef_flags |= SPH_FDEF_SPACING_2; - exc->face->sph_found_func_flags |= SPH_FDEF_SPACING_2; - } - break; - - case 7: - rec->sph_fdef_flags |= SPH_FDEF_TYPEMAN_DIAGENDCTRL; - exc->face->sph_found_func_flags |= SPH_FDEF_TYPEMAN_DIAGENDCTRL; - break; - - case 8: -#if 0 - rec->sph_fdef_flags |= SPH_FDEF_TYPEMAN_DIAGENDCTRL; - exc->face->sph_found_func_flags |= SPH_FDEF_TYPEMAN_DIAGENDCTRL; -#endif - break; - } - opcode_pointer[i] = 0; - } - } - - else - opcode_pointer[i] = 0; - } - - /* Set sph_compatibility_mode only when deltas are detected */ - exc->face->sph_compatibility_mode = - ( ( exc->face->sph_found_func_flags & SPH_FDEF_INLINE_DELTA_1 ) | - ( exc->face->sph_found_func_flags & SPH_FDEF_INLINE_DELTA_2 ) ); - } - -#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ - switch ( exc->opcode ) { case 0x89: /* IDEF */ @@ -3847,10 +3580,6 @@ TT_CallRec* pRec; -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - exc->sph_in_func_flags = 0x0000; -#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ - if ( exc->callTop <= 0 ) /* We encountered an ENDF without a call */ { exc->error = FT_THROW( ENDF_In_Exec_Stream ); @@ -3938,17 +3667,6 @@ if ( !def->active ) goto Fail; -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - if ( SUBPIXEL_HINTING_INFINALITY && - exc->ignore_x_mode && - ( ( exc->iup_called && - ( exc->sph_tweak_flags & SPH_TWEAK_NO_CALL_AFTER_IUP ) ) || - ( def->sph_fdef_flags & SPH_FDEF_VACUFORM_ROUND_1 ) ) ) - goto Fail; - else - exc->sph_in_func_flags = def->sph_fdef_flags; -#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ - /* check the call stack */ if ( exc->callTop >= exc->callSize ) { @@ -4026,15 +3744,6 @@ if ( !def->active ) goto Fail; -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - if ( SUBPIXEL_HINTING_INFINALITY && - exc->ignore_x_mode && - ( def->sph_fdef_flags & SPH_FDEF_VACUFORM_ROUND_1 ) ) - goto Fail; - else - exc->sph_in_func_flags = def->sph_fdef_flags; -#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ - /* check stack */ if ( exc->callTop >= exc->callSize ) { @@ -4940,14 +4649,6 @@ } } -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - /* Disable Type 2 Vacuform Rounds - e.g. Arial Narrow */ - if ( SUBPIXEL_HINTING_INFINALITY && - exc->ignore_x_mode && - ( D < 0 ? NEG_LONG( D ) : D ) == 64 ) - D += 1; -#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ - args[0] = D; } @@ -5209,13 +4910,6 @@ /* except to change the subpixel flags temporarily */ else if ( exc->iniRange == tt_coderange_glyph && K == 3 ) { -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - /* INSTCTRL modifying flag 3 also has an effect */ - /* outside of the CVT program */ - if ( SUBPIXEL_HINTING_INFINALITY ) - exc->ignore_x_mode = !FT_BOOL( L == 4 ); -#endif - #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL /* Native ClearType fonts sign a waiver that turns off all backward */ /* compatibility hacks and lets them program points to the grid like */ @@ -5547,12 +5241,6 @@ } } else -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - /* doesn't follow Cleartype spec but produces better result */ - if ( SUBPIXEL_HINTING_INFINALITY && exc->ignore_x_mode ) - Move_Zp2_Point( exc, point, 0, dy, TRUE ); - else -#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ Move_Zp2_Point( exc, point, dx, dy, TRUE ); exc->GS.loop--; @@ -5713,76 +5401,6 @@ } } else -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - if ( SUBPIXEL_HINTING_INFINALITY && - exc->ignore_x_mode ) - { - FT_Int B1, B2; - - - /* If not using ignore_x_mode rendering, allow ZP2 move. */ - /* If inline deltas aren't allowed, skip ZP2 move. */ - /* If using ignore_x_mode rendering, allow ZP2 point move if: */ - /* - freedom vector is y and sph_compatibility_mode is off */ - /* - the glyph is composite and the move is in the Y direction */ - /* - the glyph is specifically set to allow SHPIX moves */ - /* - the move is on a previously Y-touched point */ - - /* save point for later comparison */ - B1 = exc->zp2.cur[point].y; - - if ( exc->face->sph_compatibility_mode ) - { - if ( exc->sph_tweak_flags & SPH_TWEAK_ROUND_NONPIXEL_Y_MOVES ) - dy = FT_PIX_ROUND( B1 + dy ) - B1; - - /* skip post-iup deltas */ - if ( exc->iup_called && - ( ( exc->sph_in_func_flags & SPH_FDEF_INLINE_DELTA_1 ) || - ( exc->sph_in_func_flags & SPH_FDEF_INLINE_DELTA_2 ) ) ) - goto Skip; - - if ( !( exc->sph_tweak_flags & SPH_TWEAK_ALWAYS_SKIP_DELTAP ) && - ( ( exc->is_composite && exc->GS.freeVector.y != 0 ) || - ( exc->zp2.tags[point] & FT_CURVE_TAG_TOUCH_Y ) || - ( exc->sph_tweak_flags & SPH_TWEAK_DO_SHPIX ) ) ) - Move_Zp2_Point( exc, point, 0, dy, TRUE ); - - /* save new point */ - if ( exc->GS.freeVector.y != 0 ) - { - B2 = exc->zp2.cur[point].y; - - /* reverse any disallowed moves */ - if ( ( B1 & 63 ) == 0 && - ( B2 & 63 ) != 0 && - B1 != B2 ) - Move_Zp2_Point( exc, point, 0, NEG_LONG( dy ), TRUE ); - } - } - else if ( exc->GS.freeVector.y != 0 ) - { - Move_Zp2_Point( exc, point, dx, dy, TRUE ); - - /* save new point */ - B2 = exc->zp2.cur[point].y; - - /* reverse any disallowed moves */ - if ( ( exc->sph_tweak_flags & SPH_TWEAK_SKIP_NONPIXEL_Y_MOVES ) && - ( B1 & 63 ) != 0 && - ( B2 & 63 ) != 0 && - B1 != B2 ) - Move_Zp2_Point( exc, - point, - NEG_LONG( dx ), - NEG_LONG( dy ), - TRUE ); - } - else if ( exc->sph_in_func_flags & SPH_FDEF_TYPEMAN_DIAGENDCTRL ) - Move_Zp2_Point( exc, point, dx, dy, TRUE ); - } - else -#endif #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL if ( SUBPIXEL_HINTING_MINIMAL && exc->backward_compatibility ) @@ -5802,9 +5420,6 @@ #endif Move_Zp2_Point( exc, point, dx, dy, TRUE ); -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - Skip: -#endif exc->GS.loop--; } @@ -5849,28 +5464,6 @@ distance = PROJECT( exc->zp1.cur + point, exc->zp0.cur + exc->GS.rp0 ); -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - /* subpixel hinting - make MSIRP respect CVT cut-in; */ - if ( SUBPIXEL_HINTING_INFINALITY && - exc->ignore_x_mode && - exc->GS.freeVector.x != 0 ) - { - FT_F26Dot6 control_value_cutin = exc->GS.control_value_cutin; - FT_F26Dot6 delta; - - - if ( !( exc->sph_tweak_flags & SPH_TWEAK_NORMAL_ROUND ) ) - control_value_cutin = 0; - - delta = SUB_LONG( distance, args[1] ); - if ( delta < 0 ) - delta = NEG_LONG( delta ); - - if ( delta >= control_value_cutin ) - distance = args[1]; - } -#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ - exc->func_move( exc, &exc->zp1, point, @@ -5911,14 +5504,7 @@ if ( ( exc->opcode & 1 ) != 0 ) { cur_dist = FAST_PROJECT( &exc->zp0.cur[point] ); -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - if ( SUBPIXEL_HINTING_INFINALITY && - exc->ignore_x_mode && - exc->GS.freeVector.x != 0 ) - distance = SUB_LONG( Round_None( exc, cur_dist, 3 ), cur_dist ); - else -#endif - distance = SUB_LONG( exc->func_round( exc, cur_dist, 3 ), cur_dist ); + distance = SUB_LONG( exc->func_round( exc, cur_dist, 3 ), cur_dist ); } else distance = 0; @@ -5981,27 +5567,12 @@ if ( exc->GS.gep0 == 0 ) /* If in twilight zone */ { -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - /* Only adjust if not in sph_compatibility_mode or ignore_x_mode. */ - /* Determined via experimentation and may be incorrect... */ - if ( !( SUBPIXEL_HINTING_INFINALITY && - ( exc->ignore_x_mode && - exc->face->sph_compatibility_mode ) ) ) -#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ - exc->zp0.org[point].x = TT_MulFix14( distance, + exc->zp0.org[point].x = TT_MulFix14( distance, exc->GS.freeVector.x ); exc->zp0.org[point].y = TT_MulFix14( distance, exc->GS.freeVector.y ); exc->zp0.cur[point] = exc->zp0.org[point]; } -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - if ( SUBPIXEL_HINTING_INFINALITY && - exc->ignore_x_mode && - ( exc->sph_tweak_flags & SPH_TWEAK_MIAP_HACK ) && - distance > 0 && - exc->GS.freeVector.y != 0 ) - distance = 0; -#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ org_dist = FAST_PROJECT( &exc->zp0.cur[point] ); @@ -6011,15 +5582,6 @@ FT_F26Dot6 delta; -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - if ( SUBPIXEL_HINTING_INFINALITY && - exc->ignore_x_mode && - exc->GS.freeVector.x != 0 && - exc->GS.freeVector.y == 0 && - !( exc->sph_tweak_flags & SPH_TWEAK_NORMAL_ROUND ) ) - control_value_cutin = 0; -#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ - delta = SUB_LONG( distance, org_dist ); if ( delta < 0 ) delta = NEG_LONG( delta ); @@ -6027,14 +5589,7 @@ if ( delta > control_value_cutin ) distance = org_dist; -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - if ( SUBPIXEL_HINTING_INFINALITY && - exc->ignore_x_mode && - exc->GS.freeVector.x != 0 ) - distance = Round_None( exc, distance, 3 ); - else -#endif - distance = exc->func_round( exc, distance, 3 ); + distance = exc->func_round( exc, distance, 3 ); } exc->func_move( exc, &exc->zp0, point, SUB_LONG( distance, org_dist ) ); @@ -6127,14 +5682,7 @@ if ( ( exc->opcode & 4 ) != 0 ) { -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - if ( SUBPIXEL_HINTING_INFINALITY && - exc->ignore_x_mode && - exc->GS.freeVector.x != 0 ) - distance = Round_None( exc, org_dist, exc->opcode & 3 ); - else -#endif - distance = exc->func_round( exc, org_dist, exc->opcode & 3 ); + distance = exc->func_round( exc, org_dist, exc->opcode & 3 ); } else distance = Round_None( exc, org_dist, exc->opcode & 3 ); @@ -6146,14 +5694,6 @@ FT_F26Dot6 minimum_distance = exc->GS.minimum_distance; -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - if ( SUBPIXEL_HINTING_INFINALITY && - exc->ignore_x_mode && - exc->GS.freeVector.x != 0 && - !( exc->sph_tweak_flags & SPH_TWEAK_NORMAL_ROUND ) ) - minimum_distance = 0; -#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ - if ( org_dist >= 0 ) { if ( distance < minimum_distance ) @@ -6296,41 +5836,7 @@ distance = exc->func_round( exc, cvt_dist, exc->opcode & 3 ); } else - { - -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - /* do cvt cut-in always in MIRP for sph */ - if ( SUBPIXEL_HINTING_INFINALITY && - exc->ignore_x_mode && - exc->GS.gep0 == exc->GS.gep1 ) - { - FT_F26Dot6 control_value_cutin = exc->GS.control_value_cutin; - - - if ( exc->GS.freeVector.x != 0 && - !( exc->sph_tweak_flags & SPH_TWEAK_NORMAL_ROUND ) ) - control_value_cutin = 0; - - if ( exc->GS.freeVector.y != 0 && - ( exc->sph_tweak_flags & SPH_TWEAK_TIMES_NEW_ROMAN_HACK ) ) - { - if ( cur_dist < -64 ) - cvt_dist -= 16; - else if ( cur_dist > 64 && cur_dist < 84 ) - cvt_dist += 32; - } - - delta = SUB_LONG( cvt_dist, org_dist ); - if ( delta < 0 ) - delta = NEG_LONG( delta ); - - if ( delta > control_value_cutin ) - cvt_dist = org_dist; - } -#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ - distance = Round_None( exc, cvt_dist, exc->opcode & 3 ); - } /* minimum distance test */ @@ -6339,14 +5845,6 @@ FT_F26Dot6 minimum_distance = exc->GS.minimum_distance; -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - if ( SUBPIXEL_HINTING_INFINALITY && - exc->ignore_x_mode && - exc->GS.freeVector.x != 0 && - !( exc->sph_tweak_flags & SPH_TWEAK_NORMAL_ROUND ) ) - minimum_distance = 0; -#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ - if ( org_dist >= 0 ) { if ( distance < minimum_distance ) @@ -6359,51 +5857,10 @@ } } -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - if ( SUBPIXEL_HINTING_INFINALITY && - exc->ignore_x_mode && - exc->GS.freeVector.y != 0 ) - { - FT_Int B1, B2; - - - B1 = exc->zp1.cur[point].y; - - /* Round moves if necessary */ - if ( exc->sph_tweak_flags & SPH_TWEAK_ROUND_NONPIXEL_Y_MOVES ) - distance = FT_PIX_ROUND( B1 + distance - cur_dist ) - B1 + cur_dist; - - if ( ( exc->opcode & 16 ) == 0 && - ( exc->opcode & 8 ) == 0 && - ( exc->sph_tweak_flags & SPH_TWEAK_COURIER_NEW_2_HACK ) ) - distance += 64; - - exc->func_move( exc, - &exc->zp1, - point, - SUB_LONG( distance, cur_dist ) ); - - B2 = exc->zp1.cur[point].y; - - /* Reverse move if necessary */ - if ( ( exc->face->sph_compatibility_mode && - ( B1 & 63 ) == 0 && - ( B2 & 63 ) != 0 ) || - ( ( exc->sph_tweak_flags & SPH_TWEAK_SKIP_NONPIXEL_Y_MOVES ) && - ( B1 & 63 ) != 0 && - ( B2 & 63 ) != 0 ) ) - exc->func_move( exc, - &exc->zp1, - point, - SUB_LONG( cur_dist, distance ) ); - } - else -#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ - - exc->func_move( exc, - &exc->zp1, - point, - SUB_LONG( distance, cur_dist ) ); + exc->func_move( exc, + &exc->zp1, + point, + SUB_LONG( distance, cur_dist ) ); Fail: exc->GS.rp1 = exc->GS.rp0; @@ -6428,17 +5885,6 @@ FT_F26Dot6 distance; -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - if ( SUBPIXEL_HINTING_INFINALITY && - exc->ignore_x_mode && - exc->iup_called && - ( exc->sph_tweak_flags & SPH_TWEAK_NO_ALIGNRP_AFTER_IUP ) ) - { - exc->error = FT_THROW( Invalid_Reference ); - goto Fail; - } -#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ - if ( exc->top < exc->GS.loop || BOUNDS( exc->GS.rp0, exc->zp0.n_points ) ) { @@ -6997,16 +6443,6 @@ contour = 0; point = 0; -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - if ( SUBPIXEL_HINTING_INFINALITY && - exc->ignore_x_mode ) - { - exc->iup_called = TRUE; - if ( exc->sph_tweak_flags & SPH_TWEAK_SKIP_IUP ) - return; - } -#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ - do { end_point = exc->pts.contours[contour] - exc->pts.first_point; @@ -7079,14 +6515,6 @@ FT_Long B; -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - if ( SUBPIXEL_HINTING_INFINALITY && - exc->ignore_x_mode && - exc->iup_called && - ( exc->sph_tweak_flags & SPH_TWEAK_NO_DELTAP_AFTER_IUP ) ) - goto Fail; -#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ - P = (FT_ULong)exc->func_cur_ppem( exc ); nump = (FT_ULong)args[0]; /* some points theoretically may occur more than once, thus UShort isn't enough */ @@ -7139,87 +6567,21 @@ B++; B *= 1L << ( 6 - exc->GS.delta_shift ); -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - - if ( SUBPIXEL_HINTING_INFINALITY ) - { - /* - * Allow delta move if - * - * - not using ignore_x_mode rendering, - * - glyph is specifically set to allow it, or - * - glyph is composite and freedom vector is not in subpixel - * direction. - */ - if ( !exc->ignore_x_mode || - ( exc->sph_tweak_flags & SPH_TWEAK_ALWAYS_DO_DELTAP ) || - ( exc->is_composite && exc->GS.freeVector.y != 0 ) ) - exc->func_move( exc, &exc->zp0, A, B ); - - /* Otherwise, apply subpixel hinting and compatibility mode */ - /* rules, always skipping deltas in subpixel direction. */ - else if ( exc->ignore_x_mode && exc->GS.freeVector.y != 0 ) - { - FT_UShort B1, B2; - - - /* save the y value of the point now; compare after move */ - B1 = (FT_UShort)exc->zp0.cur[A].y; - - /* Standard subpixel hinting: Allow y move for y-touched */ - /* points. This messes up DejaVu ... */ - if ( !exc->face->sph_compatibility_mode && - ( exc->zp0.tags[A] & FT_CURVE_TAG_TOUCH_Y ) ) - exc->func_move( exc, &exc->zp0, A, B ); - - /* compatibility mode */ - else if ( exc->face->sph_compatibility_mode && - !( exc->sph_tweak_flags & SPH_TWEAK_ALWAYS_SKIP_DELTAP ) ) - { - if ( exc->sph_tweak_flags & SPH_TWEAK_ROUND_NONPIXEL_Y_MOVES ) - B = FT_PIX_ROUND( B1 + B ) - B1; - - /* Allow delta move if using sph_compatibility_mode, */ - /* IUP has not been called, and point is touched on Y. */ - if ( !exc->iup_called && - ( exc->zp0.tags[A] & FT_CURVE_TAG_TOUCH_Y ) ) - exc->func_move( exc, &exc->zp0, A, B ); - } - - B2 = (FT_UShort)exc->zp0.cur[A].y; - - /* Reverse this move if it results in a disallowed move */ - if ( exc->GS.freeVector.y != 0 && - ( ( exc->face->sph_compatibility_mode && - ( B1 & 63 ) == 0 && - ( B2 & 63 ) != 0 ) || - ( ( exc->sph_tweak_flags & - SPH_TWEAK_SKIP_NONPIXEL_Y_MOVES_DELTAP ) && - ( B1 & 63 ) != 0 && - ( B2 & 63 ) != 0 ) ) ) - exc->func_move( exc, &exc->zp0, A, NEG_LONG( B ) ); - } - } - else -#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ - - { #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL - /* See `ttinterp.h' for details on backward compatibility */ - /* mode. */ - if ( SUBPIXEL_HINTING_MINIMAL && - exc->backward_compatibility ) - { - if ( !( exc->iupx_called && exc->iupy_called ) && - ( ( exc->is_composite && exc->GS.freeVector.y != 0 ) || - ( exc->zp0.tags[A] & FT_CURVE_TAG_TOUCH_Y ) ) ) - exc->func_move( exc, &exc->zp0, A, B ); - } - else -#endif + /* See `ttinterp.h' for details on backward compatibility */ + /* mode. */ + if ( SUBPIXEL_HINTING_MINIMAL && + exc->backward_compatibility ) + { + if ( !( exc->iupx_called && exc->iupy_called ) && + ( ( exc->is_composite && exc->GS.freeVector.y != 0 ) || + ( exc->zp0.tags[A] & FT_CURVE_TAG_TOUCH_Y ) ) ) exc->func_move( exc, &exc->zp0, A, B ); } + else +#endif + exc->func_move( exc, &exc->zp0, A, B ); } } else @@ -7333,31 +6695,8 @@ K = 0; -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - /********************************* - * RASTERIZER VERSION - * Selector Bit: 0 - * Return Bit(s): 0-7 - */ - if ( SUBPIXEL_HINTING_INFINALITY && - ( args[0] & 1 ) != 0 && - exc->subpixel_hinting ) - { - if ( exc->ignore_x_mode ) - { - /* if in ClearType backward compatibility mode, */ - /* we sometimes change the TrueType version dynamically */ - K = exc->rasterizer_version; - FT_TRACE6(( "Setting rasterizer version %d\n", - exc->rasterizer_version )); - } - else - K = TT_INTERPRETER_VERSION_38; - } - else -#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ - if ( ( args[0] & 1 ) != 0 ) - K = driver->interpreter_version; + if ( ( args[0] & 1 ) != 0 ) + K = driver->interpreter_version; /********************************* * GLYPH ROTATED @@ -7454,89 +6793,6 @@ } #endif -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - - if ( SUBPIXEL_HINTING_INFINALITY && - exc->rasterizer_version >= TT_INTERPRETER_VERSION_35 ) - { - - if ( exc->rasterizer_version >= 37 ) - { - /********************************* - * HINTING FOR SUBPIXEL - * Selector Bit: 6 - * Return Bit(s): 13 - */ - if ( ( args[0] & 64 ) != 0 && exc->subpixel_hinting ) - K |= 1 << 13; - - /********************************* - * COMPATIBLE WIDTHS ENABLED - * Selector Bit: 7 - * Return Bit(s): 14 - * - * Functionality still needs to be added - */ - if ( ( args[0] & 128 ) != 0 && exc->compatible_widths ) - K |= 1 << 14; - - /********************************* - * VERTICAL LCD SUBPIXELS? - * Selector Bit: 8 - * Return Bit(s): 15 - * - * Functionality still needs to be added - */ - if ( ( args[0] & 256 ) != 0 && exc->vertical_lcd ) - K |= 1 << 15; - - /********************************* - * HINTING FOR BGR? - * Selector Bit: 9 - * Return Bit(s): 16 - * - * Functionality still needs to be added - */ - if ( ( args[0] & 512 ) != 0 && exc->bgr ) - K |= 1 << 16; - - if ( exc->rasterizer_version >= 38 ) - { - /********************************* - * SUBPIXEL POSITIONED? - * Selector Bit: 10 - * Return Bit(s): 17 - * - * Functionality still needs to be added - */ - if ( ( args[0] & 1024 ) != 0 && exc->subpixel_positioned ) - K |= 1 << 17; - - /********************************* - * SYMMETRICAL SMOOTHING - * Selector Bit: 11 - * Return Bit(s): 18 - * - * Functionality still needs to be added - */ - if ( ( args[0] & 2048 ) != 0 && exc->symmetrical_smoothing ) - K |= 1 << 18; - - /********************************* - * GRAY CLEARTYPE - * Selector Bit: 12 - * Return Bit(s): 19 - * - * Functionality still needs to be added - */ - if ( ( args[0] & 4096 ) != 0 && exc->gray_cleartype ) - K |= 1 << 19; - } - } - } - -#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ - args[0] = K; } @@ -7679,20 +6935,6 @@ FT_ULong num_twilight_points; FT_UShort i; -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - FT_Byte opcode_pattern[1][2] = - { - /* #8 TypeMan Talk Align */ - { - 0x06, /* SPVTL */ - 0x7D, /* RDTG */ - }, - }; - FT_UShort opcode_patterns = 1; - FT_UShort opcode_pointer[1] = { 0 }; - FT_UShort opcode_size[1] = { 1 }; -#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ - /* We restrict the number of twilight points to a reasonable, */ /* heuristic value to avoid slow execution of malformed bytecode. */ @@ -7770,9 +7012,6 @@ Compute_Round( exc, (FT_Byte)exc->GS.round_state ); /* These flags cancel execution of some opcodes after IUP is called */ -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - exc->iup_called = FALSE; -#endif #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL exc->iupx_called = FALSE; exc->iupy_called = FALSE; @@ -7862,39 +7101,6 @@ exc->step_ins = TRUE; exc->error = FT_Err_Ok; -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - - if ( SUBPIXEL_HINTING_INFINALITY ) - { - for ( i = 0; i < opcode_patterns; i++ ) - { - if ( opcode_pointer[i] < opcode_size[i] && - exc->opcode == opcode_pattern[i][opcode_pointer[i]] ) - { - opcode_pointer[i] += 1; - - if ( opcode_pointer[i] == opcode_size[i] ) - { - FT_TRACE6(( "sph: opcode ptrn: %d, %s %s\n", - i, - exc->face->root.family_name, - exc->face->root.style_name )); - - switch ( i ) - { - case 0: - break; - } - opcode_pointer[i] = 0; - } - } - else - opcode_pointer[i] = 0; - } - } - -#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ - { FT_Long* args = exc->stack + exc->args; FT_Byte opcode = exc->opcode; diff --git a/src/3rdparty/freetype/src/truetype/ttinterp.h b/src/3rdparty/freetype/src/truetype/ttinterp.h index 48d493af801..e98e258fe7e 100644 --- a/src/3rdparty/freetype/src/truetype/ttinterp.h +++ b/src/3rdparty/freetype/src/truetype/ttinterp.h @@ -98,48 +98,6 @@ FT_BEGIN_HEADER } TT_CallRec, *TT_CallStack; -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - - /************************************************************************** - * - * These structures define rules used to tweak subpixel hinting for - * various fonts. "", 0, "", NULL value indicates to match any value. - */ - -#define SPH_MAX_NAME_SIZE 32 -#define SPH_MAX_CLASS_MEMBERS 100 - - typedef struct SPH_TweakRule_ - { - const char family[SPH_MAX_NAME_SIZE]; - const FT_UInt ppem; - const char style[SPH_MAX_NAME_SIZE]; - const FT_ULong glyph; - - } SPH_TweakRule; - - - typedef struct SPH_ScaleRule_ - { - const char family[SPH_MAX_NAME_SIZE]; - const FT_UInt ppem; - const char style[SPH_MAX_NAME_SIZE]; - const FT_ULong glyph; - const FT_ULong scale; - - } SPH_ScaleRule; - - - typedef struct SPH_Font_Class_ - { - const char name[SPH_MAX_NAME_SIZE]; - const char member[SPH_MAX_CLASS_MEMBERS][SPH_MAX_NAME_SIZE]; - - } SPH_Font_Class; - -#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ - - /************************************************************************** * * The main structure for the interpreter which collects all necessary @@ -399,38 +357,6 @@ FT_BEGIN_HEADER FT_Bool grayscale_cleartype; #endif /* TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL */ -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - TT_Round_Func func_round_sphn; /* subpixel rounding function */ - - FT_Bool subpixel_hinting; /* Using subpixel hinting? */ - FT_Bool ignore_x_mode; /* Standard rendering mode for */ - /* subpixel hinting. On if gray */ - /* or subpixel hinting is on. */ - - /* The following 6 aren't fully implemented but here for MS rasterizer */ - /* compatibility. */ - FT_Bool compatible_widths; /* compatible widths? */ - FT_Bool symmetrical_smoothing; /* symmetrical_smoothing? */ - FT_Bool bgr; /* bgr instead of rgb? */ - FT_Bool vertical_lcd; /* long side of LCD subpixel */ - /* rectangles is horizontal */ - FT_Bool subpixel_positioned; /* subpixel positioned */ - /* (DirectWrite ClearType)? */ - FT_Bool gray_cleartype; /* ClearType hinting but */ - /* grayscale rendering */ - - FT_Int rasterizer_version; /* MS rasterizer version */ - - FT_Bool iup_called; /* IUP called for glyph? */ - - FT_ULong sph_tweak_flags; /* flags to control */ - /* hint tweaks */ - - FT_ULong sph_in_func_flags; /* flags to indicate if in */ - /* special functions */ - -#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ - /* We maintain two counters (in addition to the instruction counter) */ /* that act as loop detectors for LOOPCALL and jump opcodes with */ /* negative arguments. */ diff --git a/src/3rdparty/freetype/src/truetype/ttobjs.c b/src/3rdparty/freetype/src/truetype/ttobjs.c index 958fa54d426..5b56af711df 100644 --- a/src/3rdparty/freetype/src/truetype/ttobjs.c +++ b/src/3rdparty/freetype/src/truetype/ttobjs.c @@ -1481,9 +1481,6 @@ TT_Driver driver = (TT_Driver)ttdriver; driver->interpreter_version = TT_INTERPRETER_VERSION_35; -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - driver->interpreter_version = TT_INTERPRETER_VERSION_38; -#endif #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL driver->interpreter_version = TT_INTERPRETER_VERSION_40; #endif diff --git a/src/3rdparty/freetype/src/truetype/ttobjs.h b/src/3rdparty/freetype/src/truetype/ttobjs.h index d1834c046f3..40eb37b4c43 100644 --- a/src/3rdparty/freetype/src/truetype/ttobjs.h +++ b/src/3rdparty/freetype/src/truetype/ttobjs.h @@ -162,8 +162,6 @@ FT_BEGIN_HEADER FT_Long end; /* where does it end? */ FT_UInt opc; /* function #, or instruction code */ FT_Bool active; /* is it active? */ - FT_Bool inline_delta; /* is function that defines inline delta? */ - FT_ULong sph_fdef_flags; /* flags to identify special functions */ } TT_DefRecord, *TT_DefArray; diff --git a/src/3rdparty/freetype/src/truetype/ttsubpix.c b/src/3rdparty/freetype/src/truetype/ttsubpix.c deleted file mode 100644 index aed4a1a2748..00000000000 --- a/src/3rdparty/freetype/src/truetype/ttsubpix.c +++ /dev/null @@ -1,1013 +0,0 @@ -/**************************************************************************** - * - * ttsubpix.c - * - * TrueType Subpixel Hinting. - * - * Copyright (C) 2010-2023 by - * David Turner, Robert Wilhelm, and Werner Lemberg. - * - * This file is part of the FreeType project, and may only be used, - * modified, and distributed under the terms of the FreeType project - * license, LICENSE.TXT. By continuing to use, modify, or distribute - * this file you indicate that you have read the license and - * understand and accept it fully. - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "ttsubpix.h" - - -#if defined( TT_USE_BYTECODE_INTERPRETER ) && \ - defined( TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY ) - - /************************************************************************** - * - * These rules affect how the TT Interpreter does hinting, with the - * goal of doing subpixel hinting by (in general) ignoring x moves. - * Some of these rules are fixes that go above and beyond the - * stated techniques in the MS whitepaper on Cleartype, due to - * artifacts in many glyphs. So, these rules make some glyphs render - * better than they do in the MS rasterizer. - * - * "" string or 0 int/char indicates to apply to all glyphs. - * "-" used as dummy placeholders, but any non-matching string works. - * - * Some of this could arguably be implemented in fontconfig, however: - * - * - Fontconfig can't set things on a glyph-by-glyph basis. - * - The tweaks that happen here are very low-level, from an average - * user's point of view and are best implemented in the hinter. - * - * The goal is to make the subpixel hinting techniques as generalized - * as possible across all fonts to prevent the need for extra rules such - * as these. - * - * The rule structure is designed so that entirely new rules can easily - * be added when a new compatibility feature is discovered. - * - * The rule structures could also use some enhancement to handle ranges. - * - * ****************** WORK IN PROGRESS ******************* - */ - - /* These are `classes' of fonts that can be grouped together and used in */ - /* rules below. A blank entry "" is required at the end of these! */ -#define FAMILY_CLASS_RULES_SIZE 7 - - static const SPH_Font_Class FAMILY_CLASS_Rules - [FAMILY_CLASS_RULES_SIZE] = - { - { "MS Legacy Fonts", - { "Aharoni", - "Andale Mono", - "Andalus", - "Angsana New", - "AngsanaUPC", - "Arabic Transparent", - "Arial Black", - "Arial Narrow", - "Arial Unicode MS", - "Arial", - "Batang", - "Browallia New", - "BrowalliaUPC", - "Comic Sans MS", - "Cordia New", - "CordiaUPC", - "Courier New", - "DFKai-SB", - "David Transparent", - "David", - "DilleniaUPC", - "Estrangelo Edessa", - "EucrosiaUPC", - "FangSong_GB2312", - "Fixed Miriam Transparent", - "FrankRuehl", - "Franklin Gothic Medium", - "FreesiaUPC", - "Garamond", - "Gautami", - "Georgia", - "Gulim", - "Impact", - "IrisUPC", - "JasmineUPC", - "KaiTi_GB2312", - "KodchiangUPC", - "Latha", - "Levenim MT", - "LilyUPC", - "Lucida Console", - "Lucida Sans Unicode", - "MS Gothic", - "MS Mincho", - "MV Boli", - "Mangal", - "Marlett", - "Microsoft Sans Serif", - "Mingliu", - "Miriam Fixed", - "Miriam Transparent", - "Miriam", - "Narkisim", - "Palatino Linotype", - "Raavi", - "Rod Transparent", - "Rod", - "Shruti", - "SimHei", - "Simplified Arabic Fixed", - "Simplified Arabic", - "Simsun", - "Sylfaen", - "Symbol", - "Tahoma", - "Times New Roman", - "Traditional Arabic", - "Trebuchet MS", - "Tunga", - "Verdana", - "Webdings", - "Wingdings", - "", - }, - }, - { "Core MS Legacy Fonts", - { "Arial Black", - "Arial Narrow", - "Arial Unicode MS", - "Arial", - "Comic Sans MS", - "Courier New", - "Garamond", - "Georgia", - "Impact", - "Lucida Console", - "Lucida Sans Unicode", - "Microsoft Sans Serif", - "Palatino Linotype", - "Tahoma", - "Times New Roman", - "Trebuchet MS", - "Verdana", - "", - }, - }, - { "Apple Legacy Fonts", - { "Geneva", - "Times", - "Monaco", - "Century", - "Chalkboard", - "Lobster", - "Century Gothic", - "Optima", - "Lucida Grande", - "Gill Sans", - "Baskerville", - "Helvetica", - "Helvetica Neue", - "", - }, - }, - { "Legacy Sans Fonts", - { "Andale Mono", - "Arial Unicode MS", - "Arial", - "Century Gothic", - "Comic Sans MS", - "Franklin Gothic Medium", - "Geneva", - "Lucida Console", - "Lucida Grande", - "Lucida Sans Unicode", - "Lucida Sans Typewriter", - "Microsoft Sans Serif", - "Monaco", - "Tahoma", - "Trebuchet MS", - "Verdana", - "", - }, - }, - - { "Misc Legacy Fonts", - { "Dark Courier", "", }, }, - { "Verdana Clones", - { "DejaVu Sans", - "Bitstream Vera Sans", "", }, }, - { "Verdana and Clones", - { "DejaVu Sans", - "Bitstream Vera Sans", - "Verdana", "", }, }, - }; - - - /* Define this to force natural (i.e. not bitmap-compatible) widths. */ - /* The default leans strongly towards natural widths except for a few */ - /* legacy fonts where a selective combination produces nicer results. */ -/* #define FORCE_NATURAL_WIDTHS */ - - - /* Define `classes' of styles that can be grouped together and used in */ - /* rules below. A blank entry "" is required at the end of these! */ -#define STYLE_CLASS_RULES_SIZE 5 - - static const SPH_Font_Class STYLE_CLASS_Rules - [STYLE_CLASS_RULES_SIZE] = - { - { "Regular Class", - { "Regular", - "Book", - "Medium", - "Roman", - "Normal", - "", - }, - }, - { "Regular/Italic Class", - { "Regular", - "Book", - "Medium", - "Italic", - "Oblique", - "Roman", - "Normal", - "", - }, - }, - { "Bold/BoldItalic Class", - { "Bold", - "Bold Italic", - "Black", - "", - }, - }, - { "Bold/Italic/BoldItalic Class", - { "Bold", - "Bold Italic", - "Black", - "Italic", - "Oblique", - "", - }, - }, - { "Regular/Bold Class", - { "Regular", - "Book", - "Medium", - "Normal", - "Roman", - "Bold", - "Black", - "", - }, - }, - }; - - - /* Force special legacy fixes for fonts. */ -#define COMPATIBILITY_MODE_RULES_SIZE 1 - - static const SPH_TweakRule COMPATIBILITY_MODE_Rules - [COMPATIBILITY_MODE_RULES_SIZE] = - { - { "Verdana Clones", 0, "", 0 }, - }; - - - /* Don't do subpixel (ignore_x_mode) hinting; do normal hinting. */ -#define PIXEL_HINTING_RULES_SIZE 2 - - static const SPH_TweakRule PIXEL_HINTING_Rules - [PIXEL_HINTING_RULES_SIZE] = - { - /* these characters are almost always safe */ - { "Courier New", 12, "Italic", 'z' }, - { "Courier New", 11, "Italic", 'z' }, - }; - - - /* Subpixel hinting ignores SHPIX rules on X. Force SHPIX for these. */ -#define DO_SHPIX_RULES_SIZE 1 - - static const SPH_TweakRule DO_SHPIX_Rules - [DO_SHPIX_RULES_SIZE] = - { - { "-", 0, "", 0 }, - }; - - - /* Skip Y moves that start with a point that is not on a Y pixel */ - /* boundary and don't move that point to a Y pixel boundary. */ -#define SKIP_NONPIXEL_Y_MOVES_RULES_SIZE 4 - - static const SPH_TweakRule SKIP_NONPIXEL_Y_MOVES_Rules - [SKIP_NONPIXEL_Y_MOVES_RULES_SIZE] = - { - /* fix vwxyz thinness */ - { "Consolas", 0, "", 0 }, - /* Fix thin middle stems */ - { "Core MS Legacy Fonts", 0, "Regular", 0 }, - /* Cyrillic small letter I */ - { "Legacy Sans Fonts", 0, "", 0 }, - /* Fix artifacts with some Regular & Bold */ - { "Verdana Clones", 0, "", 0 }, - }; - - -#define SKIP_NONPIXEL_Y_MOVES_RULES_EXCEPTIONS_SIZE 1 - - static const SPH_TweakRule SKIP_NONPIXEL_Y_MOVES_Rules_Exceptions - [SKIP_NONPIXEL_Y_MOVES_RULES_EXCEPTIONS_SIZE] = - { - /* Fixes < and > */ - { "Courier New", 0, "Regular", 0 }, - }; - - - /* Skip Y moves that start with a point that is not on a Y pixel */ - /* boundary and don't move that point to a Y pixel boundary. */ -#define SKIP_NONPIXEL_Y_MOVES_DELTAP_RULES_SIZE 2 - - static const SPH_TweakRule SKIP_NONPIXEL_Y_MOVES_DELTAP_Rules - [SKIP_NONPIXEL_Y_MOVES_DELTAP_RULES_SIZE] = - { - /* Maintain thickness of diagonal in 'N' */ - { "Times New Roman", 0, "Regular/Bold Class", 'N' }, - { "Georgia", 0, "Regular/Bold Class", 'N' }, - }; - - - /* Skip Y moves that move a point off a Y pixel boundary. */ -#define SKIP_OFFPIXEL_Y_MOVES_RULES_SIZE 1 - - static const SPH_TweakRule SKIP_OFFPIXEL_Y_MOVES_Rules - [SKIP_OFFPIXEL_Y_MOVES_RULES_SIZE] = - { - { "-", 0, "", 0 }, - }; - - -#define SKIP_OFFPIXEL_Y_MOVES_RULES_EXCEPTIONS_SIZE 1 - - static const SPH_TweakRule SKIP_OFFPIXEL_Y_MOVES_Rules_Exceptions - [SKIP_OFFPIXEL_Y_MOVES_RULES_EXCEPTIONS_SIZE] = - { - { "-", 0, "", 0 }, - }; - - - /* Round moves that don't move a point to a Y pixel boundary. */ -#define ROUND_NONPIXEL_Y_MOVES_RULES_SIZE 2 - - static const SPH_TweakRule ROUND_NONPIXEL_Y_MOVES_Rules - [ROUND_NONPIXEL_Y_MOVES_RULES_SIZE] = - { - /* Droid font instructions don't snap Y to pixels */ - { "Droid Sans", 0, "Regular/Italic Class", 0 }, - { "Droid Sans Mono", 0, "", 0 }, - }; - - -#define ROUND_NONPIXEL_Y_MOVES_RULES_EXCEPTIONS_SIZE 1 - - static const SPH_TweakRule ROUND_NONPIXEL_Y_MOVES_Rules_Exceptions - [ROUND_NONPIXEL_Y_MOVES_RULES_EXCEPTIONS_SIZE] = - { - { "-", 0, "", 0 }, - }; - - - /* Allow a Direct_Move along X freedom vector if matched. */ -#define ALLOW_X_DMOVE_RULES_SIZE 1 - - static const SPH_TweakRule ALLOW_X_DMOVE_Rules - [ALLOW_X_DMOVE_RULES_SIZE] = - { - /* Fixes vanishing diagonal in 4 */ - { "Verdana", 0, "Regular", '4' }, - }; - - - /* Return MS rasterizer version 35 if matched. */ -#define RASTERIZER_35_RULES_SIZE 8 - - static const SPH_TweakRule RASTERIZER_35_Rules - [RASTERIZER_35_RULES_SIZE] = - { - /* This seems to be the only way to make these look good */ - { "Times New Roman", 0, "Regular", 'i' }, - { "Times New Roman", 0, "Regular", 'j' }, - { "Times New Roman", 0, "Regular", 'm' }, - { "Times New Roman", 0, "Regular", 'r' }, - { "Times New Roman", 0, "Regular", 'a' }, - { "Times New Roman", 0, "Regular", 'n' }, - { "Times New Roman", 0, "Regular", 'p' }, - { "Times", 0, "", 0 }, - }; - - - /* Don't round to the subpixel grid. Round to pixel grid. */ -#define NORMAL_ROUND_RULES_SIZE 1 - - static const SPH_TweakRule NORMAL_ROUND_Rules - [NORMAL_ROUND_RULES_SIZE] = - { - /* Fix serif thickness for certain ppems */ - /* Can probably be generalized somehow */ - { "Courier New", 0, "", 0 }, - }; - - - /* Skip IUP instructions if matched. */ -#define SKIP_IUP_RULES_SIZE 1 - - static const SPH_TweakRule SKIP_IUP_Rules - [SKIP_IUP_RULES_SIZE] = - { - { "Arial", 13, "Regular", 'a' }, - }; - - - /* Skip MIAP Twilight hack if matched. */ -#define MIAP_HACK_RULES_SIZE 1 - - static const SPH_TweakRule MIAP_HACK_Rules - [MIAP_HACK_RULES_SIZE] = - { - { "Geneva", 12, "", 0 }, - }; - - - /* Skip DELTAP instructions if matched. */ -#define ALWAYS_SKIP_DELTAP_RULES_SIZE 23 - - static const SPH_TweakRule ALWAYS_SKIP_DELTAP_Rules - [ALWAYS_SKIP_DELTAP_RULES_SIZE] = - { - { "Georgia", 0, "Regular", 'k' }, - /* fix various problems with e in different versions */ - { "Trebuchet MS", 14, "Regular", 'e' }, - { "Trebuchet MS", 13, "Regular", 'e' }, - { "Trebuchet MS", 15, "Regular", 'e' }, - { "Trebuchet MS", 0, "Italic", 'v' }, - { "Trebuchet MS", 0, "Italic", 'w' }, - { "Trebuchet MS", 0, "Regular", 'Y' }, - { "Arial", 11, "Regular", 's' }, - /* prevent problems with '3' and others */ - { "Verdana", 10, "Regular", 0 }, - { "Verdana", 9, "Regular", 0 }, - /* Cyrillic small letter short I */ - { "Legacy Sans Fonts", 0, "", 0x438 }, - { "Legacy Sans Fonts", 0, "", 0x439 }, - { "Arial", 10, "Regular", '6' }, - { "Arial", 0, "Bold/BoldItalic Class", 'a' }, - /* Make horizontal stems consistent with the rest */ - { "Arial", 24, "Bold", 'a' }, - { "Arial", 25, "Bold", 'a' }, - { "Arial", 24, "Bold", 's' }, - { "Arial", 25, "Bold", 's' }, - { "Arial", 34, "Bold", 's' }, - { "Arial", 35, "Bold", 's' }, - { "Arial", 36, "Bold", 's' }, - { "Arial", 25, "Regular", 's' }, - { "Arial", 26, "Regular", 's' }, - }; - - - /* Always do DELTAP instructions if matched. */ -#define ALWAYS_DO_DELTAP_RULES_SIZE 1 - - static const SPH_TweakRule ALWAYS_DO_DELTAP_Rules - [ALWAYS_DO_DELTAP_RULES_SIZE] = - { - { "-", 0, "", 0 }, - }; - - - /* Don't allow ALIGNRP after IUP. */ -#define NO_ALIGNRP_AFTER_IUP_RULES_SIZE 1 - - static const SPH_TweakRule NO_ALIGNRP_AFTER_IUP_Rules - [NO_ALIGNRP_AFTER_IUP_RULES_SIZE] = - { - /* Prevent creation of dents in outline */ - { "-", 0, "", 0 }, - }; - - - /* Don't allow DELTAP after IUP. */ -#define NO_DELTAP_AFTER_IUP_RULES_SIZE 1 - - static const SPH_TweakRule NO_DELTAP_AFTER_IUP_Rules - [NO_DELTAP_AFTER_IUP_RULES_SIZE] = - { - { "-", 0, "", 0 }, - }; - - - /* Don't allow CALL after IUP. */ -#define NO_CALL_AFTER_IUP_RULES_SIZE 1 - - static const SPH_TweakRule NO_CALL_AFTER_IUP_Rules - [NO_CALL_AFTER_IUP_RULES_SIZE] = - { - /* Prevent creation of dents in outline */ - { "-", 0, "", 0 }, - }; - - - /* De-embolden these glyphs slightly. */ -#define DEEMBOLDEN_RULES_SIZE 9 - - static const SPH_TweakRule DEEMBOLDEN_Rules - [DEEMBOLDEN_RULES_SIZE] = - { - { "Courier New", 0, "Bold", 'A' }, - { "Courier New", 0, "Bold", 'W' }, - { "Courier New", 0, "Bold", 'w' }, - { "Courier New", 0, "Bold", 'M' }, - { "Courier New", 0, "Bold", 'X' }, - { "Courier New", 0, "Bold", 'K' }, - { "Courier New", 0, "Bold", 'x' }, - { "Courier New", 0, "Bold", 'z' }, - { "Courier New", 0, "Bold", 'v' }, - }; - - - /* Embolden these glyphs slightly. */ -#define EMBOLDEN_RULES_SIZE 2 - - static const SPH_TweakRule EMBOLDEN_Rules - [EMBOLDEN_RULES_SIZE] = - { - { "Courier New", 0, "Regular", 0 }, - { "Courier New", 0, "Italic", 0 }, - }; - - - /* This is a CVT hack that makes thick horizontal stems on 2, 5, 7 */ - /* similar to Windows XP. */ -#define TIMES_NEW_ROMAN_HACK_RULES_SIZE 12 - - static const SPH_TweakRule TIMES_NEW_ROMAN_HACK_Rules - [TIMES_NEW_ROMAN_HACK_RULES_SIZE] = - { - { "Times New Roman", 16, "Italic", '2' }, - { "Times New Roman", 16, "Italic", '5' }, - { "Times New Roman", 16, "Italic", '7' }, - { "Times New Roman", 16, "Regular", '2' }, - { "Times New Roman", 16, "Regular", '5' }, - { "Times New Roman", 16, "Regular", '7' }, - { "Times New Roman", 17, "Italic", '2' }, - { "Times New Roman", 17, "Italic", '5' }, - { "Times New Roman", 17, "Italic", '7' }, - { "Times New Roman", 17, "Regular", '2' }, - { "Times New Roman", 17, "Regular", '5' }, - { "Times New Roman", 17, "Regular", '7' }, - }; - - - /* This fudges distance on 2 to get rid of the vanishing stem issue. */ - /* A real solution to this is certainly welcome. */ -#define COURIER_NEW_2_HACK_RULES_SIZE 15 - - static const SPH_TweakRule COURIER_NEW_2_HACK_Rules - [COURIER_NEW_2_HACK_RULES_SIZE] = - { - { "Courier New", 10, "Regular", '2' }, - { "Courier New", 11, "Regular", '2' }, - { "Courier New", 12, "Regular", '2' }, - { "Courier New", 13, "Regular", '2' }, - { "Courier New", 14, "Regular", '2' }, - { "Courier New", 15, "Regular", '2' }, - { "Courier New", 16, "Regular", '2' }, - { "Courier New", 17, "Regular", '2' }, - { "Courier New", 18, "Regular", '2' }, - { "Courier New", 19, "Regular", '2' }, - { "Courier New", 20, "Regular", '2' }, - { "Courier New", 21, "Regular", '2' }, - { "Courier New", 22, "Regular", '2' }, - { "Courier New", 23, "Regular", '2' }, - { "Courier New", 24, "Regular", '2' }, - }; - - -#ifndef FORCE_NATURAL_WIDTHS - - /* Use compatible widths with these glyphs. Compatible widths is always */ - /* on when doing B/W TrueType instructing, but is used selectively here, */ - /* typically on glyphs with 3 or more vertical stems. */ -#define COMPATIBLE_WIDTHS_RULES_SIZE 38 - - static const SPH_TweakRule COMPATIBLE_WIDTHS_Rules - [COMPATIBLE_WIDTHS_RULES_SIZE] = - { - { "Arial Unicode MS", 12, "Regular Class", 'm' }, - { "Arial Unicode MS", 14, "Regular Class", 'm' }, - /* Cyrillic small letter sha */ - { "Arial", 10, "Regular Class", 0x448 }, - { "Arial", 11, "Regular Class", 'm' }, - { "Arial", 12, "Regular Class", 'm' }, - /* Cyrillic small letter sha */ - { "Arial", 12, "Regular Class", 0x448 }, - { "Arial", 13, "Regular Class", 0x448 }, - { "Arial", 14, "Regular Class", 'm' }, - /* Cyrillic small letter sha */ - { "Arial", 14, "Regular Class", 0x448 }, - { "Arial", 15, "Regular Class", 0x448 }, - { "Arial", 17, "Regular Class", 'm' }, - { "DejaVu Sans", 15, "Regular Class", 0 }, - { "Microsoft Sans Serif", 11, "Regular Class", 0 }, - { "Microsoft Sans Serif", 12, "Regular Class", 0 }, - { "Segoe UI", 11, "Regular Class", 0 }, - { "Monaco", 0, "Regular Class", 0 }, - { "Segoe UI", 12, "Regular Class", 'm' }, - { "Segoe UI", 14, "Regular Class", 'm' }, - { "Tahoma", 11, "Regular Class", 0 }, - { "Times New Roman", 16, "Regular Class", 'c' }, - { "Times New Roman", 16, "Regular Class", 'm' }, - { "Times New Roman", 16, "Regular Class", 'o' }, - { "Times New Roman", 16, "Regular Class", 'w' }, - { "Trebuchet MS", 11, "Regular Class", 0 }, - { "Trebuchet MS", 12, "Regular Class", 0 }, - { "Trebuchet MS", 14, "Regular Class", 0 }, - { "Trebuchet MS", 15, "Regular Class", 0 }, - { "Ubuntu", 12, "Regular Class", 'm' }, - /* Cyrillic small letter sha */ - { "Verdana", 10, "Regular Class", 0x448 }, - { "Verdana", 11, "Regular Class", 0x448 }, - { "Verdana and Clones", 12, "Regular Class", 'i' }, - { "Verdana and Clones", 12, "Regular Class", 'j' }, - { "Verdana and Clones", 12, "Regular Class", 'l' }, - { "Verdana and Clones", 12, "Regular Class", 'm' }, - { "Verdana and Clones", 13, "Regular Class", 'i' }, - { "Verdana and Clones", 13, "Regular Class", 'j' }, - { "Verdana and Clones", 13, "Regular Class", 'l' }, - { "Verdana and Clones", 14, "Regular Class", 'm' }, - }; - - - /* Scaling slightly in the x-direction prior to hinting results in */ - /* more visually pleasing glyphs in certain cases. */ - /* This sometimes needs to be coordinated with compatible width rules. */ - /* A value of 1000 corresponds to a scaled value of 1.0. */ - -#define X_SCALING_RULES_SIZE 50 - - static const SPH_ScaleRule X_SCALING_Rules[X_SCALING_RULES_SIZE] = - { - { "DejaVu Sans", 12, "Regular Class", 'm', 950 }, - { "Verdana and Clones", 12, "Regular Class", 'a', 1100 }, - { "Verdana and Clones", 13, "Regular Class", 'a', 1050 }, - { "Arial", 11, "Regular Class", 'm', 975 }, - { "Arial", 12, "Regular Class", 'm', 1050 }, - /* Cyrillic small letter el */ - { "Arial", 13, "Regular Class", 0x43B, 950 }, - { "Arial", 13, "Regular Class", 'o', 950 }, - { "Arial", 13, "Regular Class", 'e', 950 }, - { "Arial", 14, "Regular Class", 'm', 950 }, - /* Cyrillic small letter el */ - { "Arial", 15, "Regular Class", 0x43B, 925 }, - { "Bitstream Vera Sans", 10, "Regular/Italic Class", 0, 1100 }, - { "Bitstream Vera Sans", 12, "Regular/Italic Class", 0, 1050 }, - { "Bitstream Vera Sans", 16, "Regular Class", 0, 1050 }, - { "Bitstream Vera Sans", 9, "Regular/Italic Class", 0, 1050 }, - { "DejaVu Sans", 12, "Regular Class", 'l', 975 }, - { "DejaVu Sans", 12, "Regular Class", 'i', 975 }, - { "DejaVu Sans", 12, "Regular Class", 'j', 975 }, - { "DejaVu Sans", 13, "Regular Class", 'l', 950 }, - { "DejaVu Sans", 13, "Regular Class", 'i', 950 }, - { "DejaVu Sans", 13, "Regular Class", 'j', 950 }, - { "DejaVu Sans", 10, "Regular/Italic Class", 0, 1100 }, - { "DejaVu Sans", 12, "Regular/Italic Class", 0, 1050 }, - { "Georgia", 10, "", 0, 1050 }, - { "Georgia", 11, "", 0, 1100 }, - { "Georgia", 12, "", 0, 1025 }, - { "Georgia", 13, "", 0, 1050 }, - { "Georgia", 16, "", 0, 1050 }, - { "Georgia", 17, "", 0, 1030 }, - { "Liberation Sans", 12, "Regular Class", 'm', 1100 }, - { "Lucida Grande", 11, "Regular Class", 'm', 1100 }, - { "Microsoft Sans Serif", 11, "Regular Class", 'm', 950 }, - { "Microsoft Sans Serif", 12, "Regular Class", 'm', 1050 }, - { "Segoe UI", 12, "Regular Class", 'H', 1050 }, - { "Segoe UI", 12, "Regular Class", 'm', 1050 }, - { "Segoe UI", 14, "Regular Class", 'm', 1050 }, - { "Tahoma", 11, "Regular Class", 'i', 975 }, - { "Tahoma", 11, "Regular Class", 'l', 975 }, - { "Tahoma", 11, "Regular Class", 'j', 900 }, - { "Tahoma", 11, "Regular Class", 'm', 918 }, - { "Verdana", 10, "Regular/Italic Class", 0, 1100 }, - { "Verdana", 12, "Regular Class", 'm', 975 }, - { "Verdana", 12, "Regular/Italic Class", 0, 1050 }, - { "Verdana", 13, "Regular/Italic Class", 'i', 950 }, - { "Verdana", 13, "Regular/Italic Class", 'j', 950 }, - { "Verdana", 13, "Regular/Italic Class", 'l', 950 }, - { "Verdana", 16, "Regular Class", 0, 1050 }, - { "Verdana", 9, "Regular/Italic Class", 0, 1050 }, - { "Times New Roman", 16, "Regular Class", 'm', 918 }, - { "Trebuchet MS", 11, "Regular Class", 'm', 800 }, - { "Trebuchet MS", 12, "Regular Class", 'm', 800 }, - }; - -#else - -#define COMPATIBLE_WIDTHS_RULES_SIZE 1 - - static const SPH_TweakRule COMPATIBLE_WIDTHS_Rules - [COMPATIBLE_WIDTHS_RULES_SIZE] = - { - { "-", 0, "", 0 }, - }; - - -#define X_SCALING_RULES_SIZE 1 - - static const SPH_ScaleRule X_SCALING_Rules - [X_SCALING_RULES_SIZE] = - { - { "-", 0, "", 0, 1000 }, - }; - -#endif /* FORCE_NATURAL_WIDTHS */ - - - static FT_Bool - is_member_of_family_class( const FT_String* detected_font_name, - const FT_String* rule_font_name ) - { - FT_UInt i, j; - - - /* Does font name match rule family? */ - if ( ft_strcmp( detected_font_name, rule_font_name ) == 0 ) - return TRUE; - - /* Is font name a wildcard ""? */ - if ( ft_strcmp( rule_font_name, "" ) == 0 ) - return TRUE; - - /* Is font name contained in a class list? */ - for ( i = 0; i < FAMILY_CLASS_RULES_SIZE; i++ ) - { - if ( ft_strcmp( FAMILY_CLASS_Rules[i].name, rule_font_name ) == 0 ) - { - for ( j = 0; j < SPH_MAX_CLASS_MEMBERS; j++ ) - { - if ( ft_strcmp( FAMILY_CLASS_Rules[i].member[j], "" ) == 0 ) - continue; - if ( ft_strcmp( FAMILY_CLASS_Rules[i].member[j], - detected_font_name ) == 0 ) - return TRUE; - } - } - } - - return FALSE; - } - - - static FT_Bool - is_member_of_style_class( const FT_String* detected_font_style, - const FT_String* rule_font_style ) - { - FT_UInt i, j; - - - /* Does font style match rule style? */ - if ( ft_strcmp( detected_font_style, rule_font_style ) == 0 ) - return TRUE; - - /* Is font style a wildcard ""? */ - if ( ft_strcmp( rule_font_style, "" ) == 0 ) - return TRUE; - - /* Is font style contained in a class list? */ - for ( i = 0; i < STYLE_CLASS_RULES_SIZE; i++ ) - { - if ( ft_strcmp( STYLE_CLASS_Rules[i].name, rule_font_style ) == 0 ) - { - for ( j = 0; j < SPH_MAX_CLASS_MEMBERS; j++ ) - { - if ( ft_strcmp( STYLE_CLASS_Rules[i].member[j], "" ) == 0 ) - continue; - if ( ft_strcmp( STYLE_CLASS_Rules[i].member[j], - detected_font_style ) == 0 ) - return TRUE; - } - } - } - - return FALSE; - } - - - FT_LOCAL_DEF( FT_Bool ) - sph_test_tweak( TT_Face face, - const FT_String* family, - FT_UInt ppem, - const FT_String* style, - FT_UInt glyph_index, - const SPH_TweakRule* rule, - FT_UInt num_rules ) - { - FT_UInt i; - - - /* rule checks may be able to be optimized further */ - for ( i = 0; i < num_rules; i++ ) - { - if ( family && - ( is_member_of_family_class ( family, rule[i].family ) ) ) - if ( rule[i].ppem == 0 || - rule[i].ppem == ppem ) - if ( style && - is_member_of_style_class ( style, rule[i].style ) ) - if ( rule[i].glyph == 0 || - FT_Get_Char_Index( (FT_Face)face, - rule[i].glyph ) == glyph_index ) - return TRUE; - } - - return FALSE; - } - - - static FT_UInt - scale_test_tweak( TT_Face face, - const FT_String* family, - FT_UInt ppem, - const FT_String* style, - FT_UInt glyph_index, - const SPH_ScaleRule* rule, - FT_UInt num_rules ) - { - FT_UInt i; - - - /* rule checks may be able to be optimized further */ - for ( i = 0; i < num_rules; i++ ) - { - if ( family && - ( is_member_of_family_class ( family, rule[i].family ) ) ) - if ( rule[i].ppem == 0 || - rule[i].ppem == ppem ) - if ( style && - is_member_of_style_class( style, rule[i].style ) ) - if ( rule[i].glyph == 0 || - FT_Get_Char_Index( (FT_Face)face, - rule[i].glyph ) == glyph_index ) - return rule[i].scale; - } - - return 1000; - } - - - FT_LOCAL_DEF( FT_UInt ) - sph_test_tweak_x_scaling( TT_Face face, - const FT_String* family, - FT_UInt ppem, - const FT_String* style, - FT_UInt glyph_index ) - { - return scale_test_tweak( face, family, ppem, style, glyph_index, - X_SCALING_Rules, X_SCALING_RULES_SIZE ); - } - - -#define TWEAK_RULES( x ) \ - if ( sph_test_tweak( face, family, ppem, style, glyph_index, \ - x##_Rules, x##_RULES_SIZE ) ) \ - loader->exec->sph_tweak_flags |= SPH_TWEAK_##x - -#define TWEAK_RULES_EXCEPTIONS( x ) \ - if ( sph_test_tweak( face, family, ppem, style, glyph_index, \ - x##_Rules_Exceptions, x##_RULES_EXCEPTIONS_SIZE ) ) \ - loader->exec->sph_tweak_flags &= ~SPH_TWEAK_##x - - - FT_LOCAL_DEF( void ) - sph_set_tweaks( TT_Loader loader, - FT_UInt glyph_index ) - { - TT_Face face = loader->face; - FT_String* family = face->root.family_name; - FT_UInt ppem = loader->size->metrics->x_ppem; - FT_String* style = face->root.style_name; - - - /* don't apply rules if style isn't set */ - if ( !face->root.style_name ) - return; - -#ifdef SPH_DEBUG_MORE_VERBOSE - printf( "%s,%d,%s,%c=%d ", - family, ppem, style, glyph_index, glyph_index ); -#endif - - TWEAK_RULES( PIXEL_HINTING ); - - if ( loader->exec->sph_tweak_flags & SPH_TWEAK_PIXEL_HINTING ) - { - loader->exec->ignore_x_mode = FALSE; - return; - } - - TWEAK_RULES( ALLOW_X_DMOVE ); - TWEAK_RULES( ALWAYS_DO_DELTAP ); - TWEAK_RULES( ALWAYS_SKIP_DELTAP ); - TWEAK_RULES( DEEMBOLDEN ); - TWEAK_RULES( DO_SHPIX ); - TWEAK_RULES( EMBOLDEN ); - TWEAK_RULES( MIAP_HACK ); - TWEAK_RULES( NORMAL_ROUND ); - TWEAK_RULES( NO_ALIGNRP_AFTER_IUP ); - TWEAK_RULES( NO_CALL_AFTER_IUP ); - TWEAK_RULES( NO_DELTAP_AFTER_IUP ); - TWEAK_RULES( RASTERIZER_35 ); - TWEAK_RULES( SKIP_IUP ); - - TWEAK_RULES( SKIP_OFFPIXEL_Y_MOVES ); - TWEAK_RULES_EXCEPTIONS( SKIP_OFFPIXEL_Y_MOVES ); - - TWEAK_RULES( SKIP_NONPIXEL_Y_MOVES_DELTAP ); - - TWEAK_RULES( SKIP_NONPIXEL_Y_MOVES ); - TWEAK_RULES_EXCEPTIONS( SKIP_NONPIXEL_Y_MOVES ); - - TWEAK_RULES( ROUND_NONPIXEL_Y_MOVES ); - TWEAK_RULES_EXCEPTIONS( ROUND_NONPIXEL_Y_MOVES ); - - if ( loader->exec->sph_tweak_flags & SPH_TWEAK_RASTERIZER_35 ) - { - if ( loader->exec->rasterizer_version != TT_INTERPRETER_VERSION_35 ) - { - loader->exec->rasterizer_version = TT_INTERPRETER_VERSION_35; - loader->exec->size->cvt_ready = -1; - - tt_size_ready_bytecode( - loader->exec->size, - FT_BOOL( loader->load_flags & FT_LOAD_PEDANTIC ) ); - } - else - loader->exec->rasterizer_version = TT_INTERPRETER_VERSION_35; - } - else - { - if ( loader->exec->rasterizer_version != - SPH_OPTION_SET_RASTERIZER_VERSION ) - { - loader->exec->rasterizer_version = SPH_OPTION_SET_RASTERIZER_VERSION; - loader->exec->size->cvt_ready = -1; - - tt_size_ready_bytecode( - loader->exec->size, - FT_BOOL( loader->load_flags & FT_LOAD_PEDANTIC ) ); - } - else - loader->exec->rasterizer_version = SPH_OPTION_SET_RASTERIZER_VERSION; - } - - if ( IS_HINTED( loader->load_flags ) ) - { - TWEAK_RULES( TIMES_NEW_ROMAN_HACK ); - TWEAK_RULES( COURIER_NEW_2_HACK ); - } - - if ( sph_test_tweak( face, family, ppem, style, glyph_index, - COMPATIBILITY_MODE_Rules, COMPATIBILITY_MODE_RULES_SIZE ) ) - loader->exec->face->sph_compatibility_mode = TRUE; - - - if ( IS_HINTED( loader->load_flags ) ) - { - if ( sph_test_tweak( face, family, ppem, style, glyph_index, - COMPATIBLE_WIDTHS_Rules, COMPATIBLE_WIDTHS_RULES_SIZE ) ) - loader->exec->compatible_widths |= TRUE; - } - } - -#else /* !(TT_USE_BYTECODE_INTERPRETER && */ - /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY) */ - - /* ANSI C doesn't like empty source files */ - typedef int tt_subpix_dummy_; - -#endif /* !(TT_USE_BYTECODE_INTERPRETER && */ - /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY) */ - - -/* END */ diff --git a/src/3rdparty/freetype/src/truetype/ttsubpix.h b/src/3rdparty/freetype/src/truetype/ttsubpix.h deleted file mode 100644 index 62af4c272d1..00000000000 --- a/src/3rdparty/freetype/src/truetype/ttsubpix.h +++ /dev/null @@ -1,110 +0,0 @@ -/**************************************************************************** - * - * ttsubpix.h - * - * TrueType Subpixel Hinting. - * - * Copyright (C) 2010-2023 by - * David Turner, Robert Wilhelm, and Werner Lemberg. - * - * This file is part of the FreeType project, and may only be used, - * modified, and distributed under the terms of the FreeType project - * license, LICENSE.TXT. By continuing to use, modify, or distribute - * this file you indicate that you have read the license and - * understand and accept it fully. - * - */ - - -#ifndef TTSUBPIX_H_ -#define TTSUBPIX_H_ - -#include "ttobjs.h" -#include "ttinterp.h" - - -FT_BEGIN_HEADER - - -#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY - - /************************************************************************** - * - * ID flags to identify special functions at FDEF and runtime. - * - */ -#define SPH_FDEF_INLINE_DELTA_1 0x0000001 -#define SPH_FDEF_INLINE_DELTA_2 0x0000002 -#define SPH_FDEF_DIAGONAL_STROKE 0x0000004 -#define SPH_FDEF_VACUFORM_ROUND_1 0x0000008 -#define SPH_FDEF_TTFAUTOHINT_1 0x0000010 -#define SPH_FDEF_SPACING_1 0x0000020 -#define SPH_FDEF_SPACING_2 0x0000040 -#define SPH_FDEF_TYPEMAN_STROKES 0x0000080 -#define SPH_FDEF_TYPEMAN_DIAGENDCTRL 0x0000100 - - - /************************************************************************** - * - * Tweak flags that are set for each glyph by the below rules. - * - */ -#define SPH_TWEAK_ALLOW_X_DMOVE 0x0000001UL -#define SPH_TWEAK_ALWAYS_DO_DELTAP 0x0000002UL -#define SPH_TWEAK_ALWAYS_SKIP_DELTAP 0x0000004UL -#define SPH_TWEAK_COURIER_NEW_2_HACK 0x0000008UL -#define SPH_TWEAK_DEEMBOLDEN 0x0000010UL -#define SPH_TWEAK_DO_SHPIX 0x0000020UL -#define SPH_TWEAK_EMBOLDEN 0x0000040UL -#define SPH_TWEAK_MIAP_HACK 0x0000080UL -#define SPH_TWEAK_NORMAL_ROUND 0x0000100UL -#define SPH_TWEAK_NO_ALIGNRP_AFTER_IUP 0x0000200UL -#define SPH_TWEAK_NO_CALL_AFTER_IUP 0x0000400UL -#define SPH_TWEAK_NO_DELTAP_AFTER_IUP 0x0000800UL -#define SPH_TWEAK_PIXEL_HINTING 0x0001000UL -#define SPH_TWEAK_RASTERIZER_35 0x0002000UL -#define SPH_TWEAK_ROUND_NONPIXEL_Y_MOVES 0x0004000UL -#define SPH_TWEAK_SKIP_IUP 0x0008000UL -#define SPH_TWEAK_SKIP_NONPIXEL_Y_MOVES 0x0010000UL -#define SPH_TWEAK_SKIP_OFFPIXEL_Y_MOVES 0x0020000UL -#define SPH_TWEAK_TIMES_NEW_ROMAN_HACK 0x0040000UL -#define SPH_TWEAK_SKIP_NONPIXEL_Y_MOVES_DELTAP 0x0080000UL - - - FT_LOCAL( FT_Bool ) - sph_test_tweak( TT_Face face, - const FT_String* family, - FT_UInt ppem, - const FT_String* style, - FT_UInt glyph_index, - const SPH_TweakRule* rule, - FT_UInt num_rules ); - - FT_LOCAL( FT_UInt ) - sph_test_tweak_x_scaling( TT_Face face, - const FT_String* family, - FT_UInt ppem, - const FT_String* style, - FT_UInt glyph_index ); - - FT_LOCAL( void ) - sph_set_tweaks( TT_Loader loader, - FT_UInt glyph_index ); - - - /* These macros are defined absent a method for setting them */ -#define SPH_OPTION_BITMAP_WIDTHS FALSE -#define SPH_OPTION_SET_SUBPIXEL TRUE -#define SPH_OPTION_SET_GRAYSCALE FALSE -#define SPH_OPTION_SET_COMPATIBLE_WIDTHS FALSE -#define SPH_OPTION_SET_RASTERIZER_VERSION 38 - -#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ - - -FT_END_HEADER - -#endif /* TTSUBPIX_H_ */ - - -/* END */ diff --git a/src/3rdparty/freetype/src/type1/t1load.c b/src/3rdparty/freetype/src/type1/t1load.c index b292a3cc0ea..be7cd0fd5e9 100644 --- a/src/3rdparty/freetype/src/type1/t1load.c +++ b/src/3rdparty/freetype/src/type1/t1load.c @@ -1773,7 +1773,7 @@ */ FT_TRACE0(( "parse_subrs: adjusting number of subroutines" - " (from %d to %ld)\n", + " (from %d to %zu)\n", num_subrs, ( parser->root.limit - parser->root.cursor ) >> 3 )); num_subrs = ( parser->root.limit - parser->root.cursor ) >> 3; @@ -1948,7 +1948,7 @@ if ( num_glyphs > ( limit - cur ) >> 3 ) { FT_TRACE0(( "parse_charstrings: adjusting number of glyphs" - " (from %d to %ld)\n", + " (from %d to %zu)\n", num_glyphs, ( limit - cur ) >> 3 )); num_glyphs = ( limit - cur ) >> 3; } diff --git a/src/3rdparty/freetype/src/type42/t42parse.c b/src/3rdparty/freetype/src/type42/t42parse.c index 764bbd4c4d9..f96a43b14dc 100644 --- a/src/3rdparty/freetype/src/type42/t42parse.c +++ b/src/3rdparty/freetype/src/type42/t42parse.c @@ -872,7 +872,7 @@ if ( loader->num_glyphs > ( limit - parser->root.cursor ) >> 2 ) { FT_TRACE0(( "t42_parse_charstrings: adjusting number of glyphs" - " (from %d to %ld)\n", + " (from %d to %zu)\n", loader->num_glyphs, ( limit - parser->root.cursor ) >> 2 )); loader->num_glyphs = ( limit - parser->root.cursor ) >> 2; diff --git a/src/3rdparty/harfbuzz-ng/AUTHORS b/src/3rdparty/harfbuzz-ng/AUTHORS index 81cdc4cf37f..83c0c66f99e 100644 --- a/src/3rdparty/harfbuzz-ng/AUTHORS +++ b/src/3rdparty/harfbuzz-ng/AUTHORS @@ -1,9 +1,14 @@ Behdad Esfahbod -Simon Hausmann -Martin Hosken -Jonathan Kew -Lars Knoll -Werner Lemberg -Roozbeh Pournader -Owen Taylor +David Corbett David Turner +Ebrahim Byagowi +Garret Rieger +Jonathan Kew +Khaled Hosny +Lars Knoll +Martin Hosken +Owen Taylor +Roderick Sheeter +Roozbeh Pournader +Simon Hausmann +Werner Lemberg diff --git a/src/3rdparty/harfbuzz-ng/COPYING b/src/3rdparty/harfbuzz-ng/COPYING index 9d1056f40b1..1dd917e9f2e 100644 --- a/src/3rdparty/harfbuzz-ng/COPYING +++ b/src/3rdparty/harfbuzz-ng/COPYING @@ -2,17 +2,23 @@ HarfBuzz is licensed under the so-called "Old MIT" license. Details follow. For parts of HarfBuzz that are licensed under different licenses see individual files names COPYING in subdirectories where applicable. -Copyright © 2010,2011,2012 Google, Inc. -Copyright © 2012 Mozilla Foundation +Copyright © 2010-2022 Google, Inc. +Copyright © 2015-2020 Ebrahim Byagowi +Copyright © 2019,2020 Facebook, Inc. +Copyright © 2012,2015 Mozilla Foundation Copyright © 2011 Codethink Limited Copyright © 2008,2010 Nokia Corporation and/or its subsidiary(-ies) Copyright © 2009 Keith Stribley -Copyright © 2009 Martin Hosken and SIL International +Copyright © 2011 Martin Hosken and SIL International Copyright © 2007 Chris Wilson -Copyright © 2006 Behdad Esfahbod -Copyright © 2005 David Turner -Copyright © 2004,2007,2008,2009,2010 Red Hat, Inc. -Copyright © 1998-2004 David Turner and Werner Lemberg +Copyright © 2005,2006,2020,2021,2022,2023 Behdad Esfahbod +Copyright © 2004,2007,2008,2009,2010,2013,2021,2022,2023 Red Hat, Inc. +Copyright © 1998-2005 David Turner and Werner Lemberg +Copyright © 2016 Igalia S.L. +Copyright © 2022 Matthias Clasen +Copyright © 2018,2021 Khaled Hosny +Copyright © 2018,2019,2020 Adobe, Inc +Copyright © 2013-2015 Alexei Podtelezhnikov For full copyright notices consult the individual files in the package. diff --git a/src/3rdparty/harfbuzz-ng/NEWS b/src/3rdparty/harfbuzz-ng/NEWS deleted file mode 100644 index 7434bcada03..00000000000 --- a/src/3rdparty/harfbuzz-ng/NEWS +++ /dev/null @@ -1,1808 +0,0 @@ -Overview of changes leading to 1.7.4 -Wednesday, December 20, 2017 -==================================== - -- Fix collect_glyphs() regression caused by hb_set_t changes. - - -Overview of changes leading to 1.7.3 -Monday, December 18, 2017 -==================================== - -- hb_set_t performance tuning and optimizations. -- Speed up collect_glyphs() and reject garbage data. -- In hb_coretext_font_create() set font point-size (ptem). -- Misc fixes. - - -Overview of changes leading to 1.7.2 -Monday, December 4, 2017 -==================================== - -- Optimize hb_set_add_range(). -- Misc fixes. -- New API: -hb_coretext_font_create() - - -Overview of changes leading to 1.7.1 -Tuesday, November 14, 2017 -==================================== - -- Fix atexit object destruction regression. -- Fix minor integer-overflow. - - -Overview of changes leading to 1.7.0 -Monday, November 13, 2017 -==================================== - -- Minor Indic fixes. -- Implement kerning and glyph names in hb-ot-font. -- Various DSO optimization re .data and .bss sizes. -- Make C++11 optional; build fixes. -- Mark all other backends "unsafe-to-break". -- Graphite fix. - - -Overview of changes leading to 1.6.3 -Thursday, October 26th, 2017 -==================================== - -- Fix hb_set_t some more. Should be solid now. -- Implement get_glyph_name() for hb-ot-font. -- Misc fixes. - - -Overview of changes leading to 1.6.2 -Monday, October 23nd, 2017 -==================================== - -- Yesterday's release had a bad crasher; don't use it. That's what - happens when one works on Sunday... - https://github.com/harfbuzz/harfbuzz/issues/578 -- Build fixes for FreeBSD and Chrome Android. - - -Overview of changes leading to 1.6.1 -Sunday, October 22nd, 2017 -==================================== - -- Don't skip over COMBINING GRAPHEME JOINER when ligating, etc. - To be refined: https://github.com/harfbuzz/harfbuzz/issues/554 -- Faster hb_set_t implementation. -- Don't use deprecated ICU API. -- Fix undefined-behavior in Myanmar shaper, introduced in 1.6.0 -- Deprecated API: - hb_set_invert() - - -Overview of changes leading to 1.6.0 -Friday, October the 13th, 2017 -==================================== - -- Update to Unicode 10. - -- Various Indic and Universal Shaping Engine fixes as a result of - HarfBuzz Hackfest with Jonathan Kew at Web Engines Hackfest at - the Igalia offices in A Coruña, Spain. Thanks Igalia for having - us! - -- Implement Unicode Arabic Mark Ordering Algorithm UTR#53. - -- Implement optical sizing / tracking in CoreText backend, using - new API hb_font_set_ptem(). - -- Allow notifying hb_font_t that underlying FT_Face changed sizing, - using new API hb_ft_font_changed(). - -- More Graphite backend RTL fixes. - -- Fix caching of variable font shaping plans. - -- hb-view / hb-shape now accept following new arguments: - - o --unicodes: takes a list of hex numbers that represent Unicode - codepoints. - -New API: -+hb_face_get_table_tags() -+hb_font_set_ptem() -+hb_font_get_ptem() -+hb_ft_font_changed() - - -Overview of changes leading to 1.5.1 -Tuesday, September 5, 2017 -==================================== - -- Fix "unsafe-to-break" in fallback shaping and other corner cases. - All our tests pass with --verify now, meaning unsafe-to-break API - works as expected. -- Add --unicodes to hb-view / hb-shape. -- [indic] Treat Consonant_With_Stacker as consonant. This will need - further tweaking. -- hb_buffer_diff() tweaks. - - -Overview of changes leading to 1.5.0 -Wednesday, August 23, 2017 -==================================== - -- Misc new API, for appending a buffer to another, and for comparing - contents of two buffers for types of differences. - -- New "unsafe-to-break" API. Can be used to speed up reshaping - in line-breaking situations. Essentially, after shaping, it returns - positions in the input string (some of the cluster boundaries) that - are "safe to break" in that if the text is segmented at that position - and two sides reshaped and concatenated, the shaping result is - exactly the same as shaping the text in one piece. - - hb-view and hb-shape and hb-shape now take --verify, which verifies - the above property. - - Some corner cases of the implementation are still not quite working. - Those will be fixed in subsequent releases. - -- New API: - -hb_buffer_append() - -hb_glyph_flags_t -HB_GLYPH_FLAG_UNSAFE_TO_BREAK -HB_GLYPH_FLAG_DEFINED -hb_glyph_info_get_glyph_flags() - -HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS - -hb_buffer_diff_flags_t -HB_BUFFER_DIFF_FLAG_EQUAL -HB_BUFFER_DIFF_FLAG_CONTENT_TYPE_MISMATCH -HB_BUFFER_DIFF_FLAG_LENGTH_MISMATCH -HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT -HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT -HB_BUFFER_DIFF_FLAG_CODEPOINT_MISMATCH -HB_BUFFER_DIFF_FLAG_CLUSTER_MISMATCH -HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH -HB_BUFFER_DIFF_FLAG_POSITION_MISMATCH -hb_buffer_diff - - -Overview of changes leading to 1.4.8 -Tuesday, August 8, 2017 -==================================== - -- Major fix to avar table handling. -- Rename hb-shape --show-message to --trace. -- Build fixes. - - -Overview of changes leading to 1.4.7 -Tuesday, July 18, 2017 -==================================== - -- Multiple Indic, Tibetan, and Cham fixes. -- CoreText: Allow disabling kerning. -- Adjust Arabic feature order again. -- Misc build fixes. - - -Overview of changes leading to 1.4.6 -Sunday, April 23, 2017 -==================================== - -- Graphite2: Fix RTL positioning issue. -- Backlist GDEF of more versions of Padauk and Tahoma. -- New, experimental, cmake alternative build system. - - -Overview of changes leading to 1.4.5 -Friday, March 10, 2017 -==================================== - -- Revert "Fix Context lookup application when moving back after a glyph..." - This introduced memory access problems. To be fixed properly soon. - - -Overview of changes leading to 1.4.4 -Sunday, March 5, 2017 -==================================== - -- Fix Context lookup application when moving back after a glyph deletion. -- Fix buffer-overrun in Bengali. - - -Overview of changes leading to 1.4.3 -Saturday, February 25, 2017 -==================================== - -- Route Adlam script to Arabic shaper. -- Misc fixes. -- New API: - hb_font_set_face() -- Deprecate API: - hb_graphite2_font_get_gr_font() - - -Overview of changes leading to 1.4.2 -Monday, January 23, 2017 -==================================== - -- Implement OpenType Font Variation tables avar/fvar/HVAR/VVAR. -- hb-shape and hb-view now accept --variations. -- New API: - -hb_variation_t -hb_variation_from_string() -hb_variation_to_string() - -hb_font_set_variations() -hb_font_set_var_coords_design() -hb_font_get_var_coords_normalized() - -hb-ot-var.h: -hb_ot_var_axis_t -hb_ot_var_has_data() -hb_ot_var_get_axis_count() -hb_ot_var_get_axes() -hb_ot_var_find_axis() -hb_ot_var_normalize_variations() -hb_ot_var_normalize_coords() - -- MVAR to be implemented later. Access to named instances to be - implemented later as well. - -- Misc fixes. - - -Overview of changes leading to 1.4.1 -Thursday, January 5, 2017 -==================================== - -- Always build and use UCDN for Unicode data by default. - Reduces dependence on version of Unicode data in glib, - specially in the Windows bundles we are shipping, which - have very old glib. - - -Overview of changes leading to 1.4.0 -Thursday, January 5, 2017 -==================================== - -- Merged "OpenType GX" branch which adds core of support for - OpenType 1.8 Font Variations. To that extent, the relevant - new API is: - -New API: -hb_font_set_var_coords_normalized() - - with supporting API: - -New API: -HB_OT_LAYOUT_NO_VARIATIONS_INDEX -hb_ot_layout_table_find_feature_variations() -hb_ot_layout_feature_with_variations_get_lookups() -hb_shape_plan_create2() -hb_shape_plan_create_cached2() - - Currently variations in GSUB/GPOS/GDEF are fully supported, - and no other tables are supported. In particular, fvar/avar - are NOT supported, hence the hb_font_set_var_coords_normalized() - taking normalized coordinates. API to take design coordinates - will be added in the future. - - HVAR/VVAR/MVAR support will also be added to hb-ot-font in the - future. - -- Fix regression in GDEF glyph class processing. -- Add decompositions for Chakma, Limbu, and Balinese in USE shaper. -- Misc fixes. - - -Overview of changes leading to 1.3.4 -Monday, December 5, 2016 -==================================== - -- Fix vertical glyph origin in hb-ot-font. -- Implement CBDT/CBLC color font glyph extents in hb-ot-font. - - -Overview of changes leading to 1.3.3 -Wednesday, September 28, 2016 -==================================== - -- Implement parsing of OpenType MATH table. -New API: -HB_OT_TAG_MATH -HB_OT_MATH_SCRIPT -hb_ot_math_constant_t -hb_ot_math_kern_t -hb_ot_math_glyph_variant_t -hb_ot_math_glyph_part_flags_t -hb_ot_math_glyph_part_t -hb_ot_math_has_data -hb_ot_math_get_constant -hb_ot_math_get_glyph_italics_correction -hb_ot_math_get_glyph_top_accent_attachment -hb_ot_math_get_glyph_kerning -hb_ot_math_is_glyph_extended_shape -hb_ot_math_get_glyph_variants -hb_ot_math_get_min_connector_overlap -hb_ot_math_get_glyph_assembly - - -Overview of changes leading to 1.3.2 -Wednesday, September 27, 2016 -==================================== - -- Fix build of hb-coretext on older OS X versions. - - -Overview of changes leading to 1.3.1 -Wednesday, September 7, 2016 -==================================== - -- Blacklist bad GDEF of more fonts (Padauk). -- More CoreText backend crash fixes with OS X 10.9.5. -- Misc fixes. - - -Overview of changes leading to 1.3.0 -Thursday, July 21, 2016 -==================================== - -- Update to Unicode 9.0.0 -- Move Javanese from Indic shaper to Universal Shaping Engine. -- Allow MultipleSubst to delete a glyph (matching Windows engine). -- Update Universal Shaping Engine to latest draft from Microsoft. -- DirectWrite backend improvements. Note: this backend is for testing ONLY. -- CoreText backend improvements with unreachable fonts. -- Implement symbol fonts (cmap 3.0.0) in hb-ft and hb-ot-font. -- Blacklist bad GDEF of more fonts (Tahoma & others). -- Misc fixes. - - -Overview of changes leading to 1.2.7 -Monday, May 2, 2016 -==================================== - -- Blacklist another version of Times New Roman (Bold) Italic from Windows 7. -- Fix Mongolian Free Variation Selectors shaping with certain fonts. -- Fix Tibetan shorthand contractions shaping. -- Improved list of language tag mappings. -- Unbreak build on Windows CE. -- Make 'glyf' table loading lazy in hb-ot-font. - - -Overview of changes leading to 1.2.6 -Friday, April 8, 2016 -==================================== - -- Blacklist GDEF table of another set of Times New Roman (Bold) Italic. -- DirectWrite backend improvements. Note: DirectWrite backend is - exclusively for our internal testing and should NOT be used in any - production system whatsoever. - - -Overview of changes leading to 1.2.5 -Monday, April 4, 2016 -==================================== - -- Fix GDEF mark-filtering-set, which was broken in 1.2.3. - - -Overview of changes leading to 1.2.4 -Thursday, March 17, 2016 -==================================== - -- Synthesize GDEF glyph class for any glyph that does not have one in GDEF. - I really hope we don't discover broken fonts that shape badly with this - change. -- Misc build and other minor fixes. -- API changes: - - Added HB_NDEBUG. It's fine for production systems to define this to - disable high-overhead debugging checks. However, I also reduced the - overhead of those checks, so it's a non-issue right now. You can - forget it. Just not defining anything at all is fine. - - -Overview of changes leading to 1.2.3 -Thursday, February 25, 2016 -==================================== - -- Blacklist GDEF table of certain versions of Times New Roman (Bold) Italic, - due to bug in glyph class of ASCII double-quote character. This should - address "regression" introduced in 1.2.0 when we switched mark zeroing - in most shapers from BY_UNICODE_LATE to BY_GDEF_LATE. - This fourth release in a week should finally stablize things... - -- hb-ot-font's get_glyph() implementation saw some optimizations. Though, - might be really hard to measure in real-world situations. - -- Also, two rather small API changes: - -We now disable some time-consuming internal bookkeeping if built with NDEBUG -defined. This is a first time that we use NDEBUG to disable debug code. If -there exist production systems that do NOT want to enable NDEBUG, please let -me know and I'll add HB_NDEBUG. - -Added get_nominal_glyph() and get_variation_glyph() instead of get_glyph() - -New API: -- hb_font_get_nominal_glyph_func_t -- hb_font_get_variation_glyph_func_t -- hb_font_funcs_set_nominal_glyph_func() -- hb_font_funcs_set_variation_glyph_func() -- hb_font_get_nominal_glyph() -- hb_font_get_variation_glyph() - -Deprecated API: -- hb_font_get_glyph_func_t -- hb_font_funcs_set_glyph_func() - -Clients that implement their own font-funcs are encouraged to replace -their get_glyph() implementation with a get_nominal_glyph() and -get_variation_glyph() pair. The variation version can assume that -variation_selector argument is not zero. Old (deprecated) functions -will continue working indefinitely using internal gymnastics; it is -just more efficient to use the new functions. - - -Overview of changes leading to 1.2.2 -Wednesday, February 24, 2016 -==================================== - -- Fix regression with mark positioning with fonts that have - non-zero mark advances. This was introduced in 1.2.0 while - trying to make mark and cursive attachments to work together. - I have partially reverted that, so this version is much more - like what we had before. All clients who updated to 1.2.0 - should update to this version. - - -Overview of changes leading to 1.2.1 -Tuesday, February 23, 2016 -==================================== - -- CoreText: Fix bug with wrong scale if font scale was changed later. - https://github.com/libass/libass/issues/212 -- CoreText: Drastically speed up font initialization. -- CoreText: Fix tiny leak. -- Group ZWJ/ZWNJ with previous syllable under cluster-level=0. - https://github.com/harfbuzz/harfbuzz/issues/217 -- Add test/shaping/README.md about how to add tests to the suite. - - -Overview of changes leading to 1.2.0 -Friday, February 19, 2016 -==================================== - -- Fix various issues (hangs mostly) in case of memory allocation failure. -- Change mark zeroing types of most shapers from BY_UNICODE_LATE to - BY_GDEF_LATE. This seems to be what Uniscribe does. -- Change mark zeroing of USE shaper from NONE to BY_GDEF_EARLY. That's - what Windows does. -- Allow GPOS cursive connection on marks, and fix the interaction with - mark attachment. This work resulted in some changes to how mark - attachments work. See: - https://github.com/harfbuzz/harfbuzz/issues/211 - https://github.com/harfbuzz/harfbuzz/commit/86c68c7a2c971efe8e35b1f1bd99401dc8b688d2 -- Graphite2 shaper: improved negative advance handling (eg. Nastaliq). -- Add nmake-based build system for Windows. -- Minor speedup. -- Misc. improvements. - - -Overview of changes leading to 1.1.3 -Monday, January 11, 2016 -==================================== - -- Ported Indic shaper to Unicode 8.0 data. -- Universal Shaping Engine fixes. -- Speed up CoreText shaper when font fallback happens in CoreText. -- Documentation improvements, thanks to Khaled Hosny. -- Very rough directwrite shaper for testing, thanks to Ebrahim Byagowi. -- Misc bug fixes. -- New API: - - * Font extents: - hb_font_extents_t - hb_font_get_font_extents_func_t - hb_font_get_font_h_extents_func_t - hb_font_get_font_v_extents_func_t - hb_font_funcs_set_font_h_extents_func - hb_font_funcs_set_font_v_extents_func - hb_font_get_h_extents - hb_font_get_v_extents - hb_font_get_extents_for_direction - - * Buffer message (aka debug): - hb_buffer_message_func_t - hb_buffer_set_message_func() - Actual message protocol to be fleshed out later. - - -Overview of changes leading to 1.1.2 -Wednesday, November 26, 2015 -==================================== - -- Fix badly-broken fallback shaper that affected terminology. - https://github.com/harfbuzz/harfbuzz/issues/187 -- Fix y_scaling in Graphite shaper. -- API changes: - * An unset glyph_h_origin() function in font-funcs now (sensibly) - implies horizontal origin at 0,0. Ie, the nil callback returns - true instead of false. As such, implementations that have a - glyph_h_origin() that simply returns true, can remove that function - with HarfBuzz >= 1.1.2. This results in a tiny speedup. - - -Overview of changes leading to 1.1.1 -Wednesday, November 24, 2015 -==================================== - -- Build fixes, specially for hb-coretext. - - -Overview of changes leading to 1.1.0 -Wednesday, November 18, 2015 -==================================== - -- Implement 'stch' stretch feature for Syriac Abbreviation Mark. - https://github.com/harfbuzz/harfbuzz/issues/141 -- Disable use of decompose_compatibility() callback. -- Implement "shaping" of various Unicode space characters, even - if the font does not support them. - https://github.com/harfbuzz/harfbuzz/issues/153 -- If font does not support U+2011 NO-BREAK HYPHEN, fallback to - U+2010 HYPHEN. -- Changes resulting from libFuzzer continuous fuzzing: - * Reject font tables that need more than 8 edits, - * Bound buffer growth during shaping to 32x, - * Fix assertions and other issues at OOM / buffer max-growth. -- Misc fixes and optimizations. -- API changes: - * All fonts created with hb_font_create() now inherit from - (ie. have parent) hb_font_get_empty(). - - -Overview of changes leading to 1.0.6 -Thursday, October 15, 2015 -==================================== - -- Reduce max nesting level in OT lookups from 8 to 6. - Should not affect any real font as far as I know. -- Fix memory access issue in ot-font. -- Revert default load-flags of fonts created using hb_ft_font_create() - back to FT_LOAD_DEFAULT|FT_LOAD_NO_HINTING. This was changed in - last release (1.0.5), but caused major issues, so revert. - https://github.com/harfbuzz/harfbuzz/issues/143 - - -Overview of changes leading to 1.0.5 -Tuesday, October 13, 2015 -==================================== - -- Fix multiple memory access bugs discovered using libFuzzer. - https://github.com/harfbuzz/harfbuzz/issues/139 - Everyone should upgrade to this version as soon as possible. - We now have continuous fuzzing set up, to avoid issues like - these creeping in again. -- Misc fixes. - -- New API: - * hb_font_set_parent(). - * hb_ft_font_[sg]et_load_flags() - The default flags for fonts created using hb_ft_font_create() - has changed to default to FT_LOAD_DEFAULT now. Previously it - was defaulting to FT_LOAD_DFEAULT|FT_LOAD_NO_HINTING. - -- API changes: - * Fonts now default to units-per-EM as their scale, instead of 0. - * hb_font_create_sub_font() does NOT make parent font immutable - anymore. hb_font_make_immutable() does. - - -Overview of changes leading to 1.0.4 -Wednesday, September 30, 2015 -==================================== - -- Fix minor out-of-bounds read error. - - -Overview of changes leading to 1.0.3 -Tuesday, September 1, 2015 -==================================== - -- Start of user documentation, from Simon Cozens! -- Implement glyph_extents() for TrueType fonts in hb-ot-font. -- Improve GPOS cursive attachments with conflicting lookups. -- More fixes for cluster-level = 1. -- Uniscribe positioning fix. - - -Overview of changes leading to 1.0.2 -Wednesday, August 19, 2015 -==================================== - -- Fix shaping with cluster-level > 0. -- Fix Uniscribe backend font-size scaling. -- Declare dependencies in harfbuzz.pc. - FreeType is not declared though, to avoid bugs in pkg-config - 0.26 with recursive dependencies. -- Slightly improved debug infrastructure. More to come later. -- Misc build fixes. - - -Overview of changes leading to 1.0.1 -Monday, July 27, 2015 -==================================== - -- Fix out-of-bounds access in USE shaper. - - -Overview of changes leading to 1.0.0 -Sunday, July 26, 2015 -==================================== - -- Implement Universal Shaping Engine: - https://www.microsoft.com/typography/OpenTypeDev/USE/intro.htm - http://blogs.windows.com/bloggingwindows/2015/02/23/windows-shapes-the-worlds-languages/ -- Bump version to 1.0.0. The soname was NOT bumped. - - -Overview of changes leading to 0.9.42 -Thursday, July 26, 2015 -===================================== - -- New API to allow for retrieving finer-grained cluster - mappings if the client desires to handle them. Default - behavior is unchanged. -- Fix cluster merging when removing default-ignorables. -- Update to Unicode 8.0 -- hb-graphite2 fixes. -- Misc fixes. -- Removed HB_NO_MERGE_CLUSTERS hack. -- New API: - hb_buffer_cluster_level_t enum - hb_buffer_get_cluster_level() - hb_buffer_set_cluster_level() - hb-shape / hb-view --cluster-level - - -Overview of changes leading to 0.9.41 -Thursday, June 18, 2015 -===================================== - -- Fix hb-coretext with trailing whitespace in right-to-left. -- New API: hb_buffer_reverse_range(). -- Allow implementing atomic ops in config.h. -- Fix hb_language_t in language bindings. -- Misc fixes. - - -Overview of changes leading to 0.9.40 -Friday, March 20, 2015 -===================================== - -- Another hb-coretext crasher fix. Ouch! -- Happy Norouz! - - -Overview of changes leading to 0.9.39 -Wednesday, March 4, 2015 -===================================== - -- Critical hb-coretext fixes. -- Optimizations and refactoring; no functional change - expected. -- Misc build fixes. - - -Overview of changes leading to 0.9.38 -Friday, January 23, 2015 -===================================== - -- Fix minor out-of-bounds access in Indic shaper. -- Change New Tai Lue shaping engine from South-East Asian to default, - reflecting change in Unicode encoding model. -- Add hb-shape --font-size. Can take up to two numbers for separate - x / y size. -- Fix CoreText and FreeType scale issues with negative scales. -- Reject blobs larger than 2GB. This might break some icu-le-hb clients - that need security fixes. See: - http://www.icu-project.org/trac/ticket/11450 -- Avoid accessing font tables during face destruction, in casce rogue - clients released face data already. -- Fix up gobject-introspection a bit. Python bindings kinda working. - See README.python. -- Misc fixes. -- API additions: - hb_ft_face_create_referenced() - hb_ft_font_create_referenced() - - -Overview of changes leading to 0.9.37 -Wednesday, December 17, 2014 -===================================== - -- Fix out-of-bounds access in Context lookup format 3. -- Indic: Allow ZWJ/ZWNJ before syllable modifiers. - - -Overview of changes leading to 0.9.36 -Thursday, November 20, 2014 -===================================== - -- First time that three months went by without a release since - 0.9.2 was released on August 10, 2012! -- Fix performance bug in hb_ot_collect_glyphs(): - https://bugzilla.mozilla.org/show_bug.cgi?id=1090869 -- Add basic vertical-text support to hb-ot-font. -- Misc build fixes. - - -Overview of changes leading to 0.9.35 -Saturday, August 13, 2014 -===================================== - -- Fix major shape-plan caching bug when more than one shaper were - provided to hb_shape_full() (as exercised by XeTeX). - http://www.mail-archive.com/debian-bugs-dist@lists.debian.org/msg1246370.html -- Fix Arabic fallback shaping regression. This was broken in 0.9.32. -- Major hb-coretext fixes. That backend is complete now, including - respecing buffer direction and language, down to vertical writing. -- Build fixes for Windows CE. Should build fine now. -- Misc fixes: - Use atexit() only if it's safe to call from shared library - https://bugs.freedesktop.org/show_bug.cgi?id=82246 - Mandaic had errors in its Unicode Joining_Type - https://bugs.freedesktop.org/show_bug.cgi?id=82306 -- API changes: - - * hb_buffer_clear_contents() does not reset buffer flags now. - - After 763e5466c0a03a7c27020e1e2598e488612529a7, one doesn't - need to set flags for different pieces of text. The flags now - are something the client sets up once, depending on how it - actually uses the buffer. As such, don't clear it in - clear_contents(). - - I don't expect any changes to be needed to any existing client. - - -Overview of changes leading to 0.9.34 -Saturday, August 2, 2014 -===================================== - -- hb_feature_from_string() now accepts CSS font-feature-settings format. -- As a result, hb-shape / hb-view --features also accept CSS-style strings. - Eg, "'liga' off" is accepted now. -- Add old-spec Myanmar shaper: - https://bugs.freedesktop.org/show_bug.cgi?id=81775 -- Don't apply 'calt' in Hangul shaper. -- Fix mark advance zeroing for Hebrew shaper: - https://bugs.freedesktop.org/show_bug.cgi?id=76767 -- Implement Windows-1256 custom Arabic shaping. Only built on Windows, - and requires help from get_glyph(). Used by Firefox. - https://bugzilla.mozilla.org/show_bug.cgi?id=1045139 -- Disable 'liga' in vertical text. -- Build fixes. -- API changes: - - * Make HB_BUFFER_FLAG_BOT/EOT easier to use. - - Previously, we expected users to provide BOT/EOT flags when the - text *segment* was at paragraph boundaries. This meant that for - clients that provide full paragraph to HarfBuzz (eg. Pango), they - had code like this: - - hb_buffer_set_flags (hb_buffer, - (item_offset == 0 ? HB_BUFFER_FLAG_BOT : 0) | - (item_offset + item_length == paragraph_length ? - HB_BUFFER_FLAG_EOT : 0)); - - hb_buffer_add_utf8 (hb_buffer, - paragraph_text, paragraph_length, - item_offset, item_length); - - After this change such clients can simply say: - - hb_buffer_set_flags (hb_buffer, - HB_BUFFER_FLAG_BOT | HB_BUFFER_FLAG_EOT); - - hb_buffer_add_utf8 (hb_buffer, - paragraph_text, paragraph_length, - item_offset, item_length); - - Ie, HarfBuzz itself checks whether the segment is at the beginning/end - of the paragraph. Clients that only pass item-at-a-time to HarfBuzz - continue not setting any flags whatsoever. - - Another way to put it is: if there's pre-context text in the buffer, - HarfBuzz ignores the BOT flag. If there's post-context, it ignores - EOT flag. - - -Overview of changes leading to 0.9.33 -Tuesday, July 22, 2014 -===================================== - -- Turn off ARabic 'cswh' feature that was accidentally turned on. -- Add HB_TAG_MAX_SIGNED. -- Make hb_face_make_immutable() really make face immutable! -- Windows build fixes. - - -Overview of changes leading to 0.9.32 -Thursday, July 17, 2014 -===================================== - -- Apply Arabic shaping features in spec order exactly. -- Another fix for Mongolian free variation selectors. -- For non-Arabic scripts in Arabic shaper apply 'rlig' and 'calt' - together. -- Minor adjustment to U+FFFD logic. -- Fix hb-coretext build. - - -Overview of changes leading to 0.9.31 -Wednesday, July 16, 2014 -===================================== - -- Only accept valid UTF-8/16/32; we missed many cases before. -- Better shaping of invalid UTF-8/16/32. Falls back to - U+FFFD REPLACEMENT CHARACTER now. -- With all changes in this release, the buffer will contain fully - valid Unicode after hb_buffer_add_utf8/16/32 no matter how - broken the input is. This can be overridden though. See below. -- Fix Mongolian Variation Selectors for fonts without GDEF. -- Fix minor invalid buffer access. -- Accept zh-Hant and zh-Hans language tags. hb_ot_tag_to_language() - now uses these instead of private tags. -- Build fixes. -- New API: - * hb_buffer_add_codepoints(). This does what hb_buffer_add_utf32() - used to do, ie. no validity check on the input at all. add_utf32 - now replaces invalid Unicode codepoints with the replacement - character (see below). - * hb_buffer_set_replacement_codepoint() - * hb_buffer_get_replacement_codepoint() - Previously, in hb_buffer_add_utf8 and hb_buffer_add_utf16, when - we detected broken input, we replaced that with (hb_codepoint_t)-1. - This has changed to use U+FFFD now, but can be changed using these - new API. - - -Overview of changes leading to 0.9.30 -Wednesday, July 9, 2014 -===================================== - -- Update to Unicode 7.0.0: - * New scripts Manichaean and Psalter Pahlavi are shaped using - Arabic shaper. - * All the other new scripts to through the generic shaper for - now. -- Minor Indic improvements. -- Fix graphite2 backend cluster mapping [crasher!] -- API changes: - * New HB_SCRIPT_* values for Unicode 7.0 scripts. - * New function hb_ot_layout_language_get_required_feature(). -- Build fixes. - - -Overview of changes leading to 0.9.29 -Thursday, May 29, 2014 -===================================== - -- Implement cmap in hb-ot-font.h. No variation-selectors yet. -- Myanmar: Allow MedialYa+Asat. -- Various Indic fixes: - * Support most characters in Extended Devanagary and Vedic - Unicode blocks. - * Allow digits and a some punctuation as consonant placeholders. -- Build fixes. - - -Overview of changes leading to 0.9.28 -Monday, April 28, 2014 -===================================== - -- Unbreak old-spec Indic shaping. (bug 76705) -- Fix shaping of U+17DD and U+0FC6. -- Add HB_NO_MERGE_CLUSTERS build option. NOT to be enabled by default - for shipping libraries. It's an option for further experimentation - right now. When we are sure how to do it properly, we will add - public run-time API for the functionality. -- Build fixes. - - -Overview of changes leading to 0.9.27 -Tuesday, March 18, 2014 -===================================== - -- Don't use "register" storage class specifier -- Wrap definition of free_langs() with HAVE_ATEXIT -- Add coretext_aat shaper and hb_coretext_face_create() constructor -- If HAVE_ICU_BUILTIN is defined, use hb-icu Unicode callbacks -- Add Myanmar test case from OpenType Myanmar spec -- Only do fallback Hebrew composition if no GPOS 'mark' available -- Allow bootstrapping without gtk-doc -- Use AM_MISSING_PROG for ragel and git -- Typo in ucdn's Makefile.am -- Improve MemoryBarrier() implementation - - -Overview of changes leading to 0.9.26 -Thursday, January 30, 2014 -===================================== - -- Misc fixes. -- Fix application of 'rtlm' feature. -- Automatically apply frac/numr/dnom around U+2044 FRACTION SLASH. -- New header: hb-ot-shape.h -- Uniscribe: fix scratch-buffer accounting. -- Reorder Tai Tham SAKOT to after tone-marks. -- Add Hangul shaper. -- New files: - hb-ot-shape-complex-hangul.cc - hb-ot-shape-complex-hebrew.cc - hb-ot-shape-complex-tibetan.cc -- Disable 'cswh' feature in Arabic shaper. -- Coretext: better handle surrogate pairs. -- Add HB_TAG_MAX and _HB_SCRIPT_MAX_VALUE. - - -Overview of changes leading to 0.9.25 -Wednesday, December 4, 2013 -===================================== - -- Myanmar shaper improvements. -- Avoid font fallback in CoreText backend. -- Additional OpenType language tag mappiongs. -- More aggressive shape-plan caching. -- Build with / require automake 1.13. -- Build with libtool 2.4.2.418 alpha to support ppc64le. - - -Overview of changes leading to 0.9.24 -Tuesday, November 13, 2013 -===================================== - -- Misc compiler warning fixes with clang. -- No functional changes. - - -Overview of changes leading to 0.9.23 -Monday, October 28, 2013 -===================================== - -- "Udupi HarfBuzz Hackfest", Paris, October 14..18 2013. -- Fix (Chain)Context recursion with non-monotone lookup positions. -- Misc Indic bug fixes. -- New Javanese / Buginese shaping, similar to Windows 8.1. - - -Overview of changes leading to 0.9.22 -Thursday, October 3, 2013 -===================================== - -- Fix use-after-end-of-scope in hb_language_from_string(). -- Fix hiding of default_ignorables if font doesn't have space glyph. -- Protect against out-of-range lookup indices. - -- API Changes: - - * Added hb_ot_layout_table_get_lookup_count() - - -Overview of changes leading to 0.9.21 -Monday, September 16, 2013 -===================================== - -- Rename gobject-introspection library name from harfbuzz to HarfBuzz. -- Remove (long disabled) hb-old and hb-icu-le test shapers. -- Misc gtk-doc and gobject-introspection annotations. -- Misc fixes. -- API changes: - - * Add HB_SET_VALUE_INVALID - -Overview of changes leading to 0.9.20 -Thursday, August 29, 2013 -===================================== - -General: -- Misc substitute_closure() fixes. -- Build fixes. - -Documentation: -- gtk-doc boilerplate integrated. Docs are built now, but - contain no contents. By next release hopefully we have - some content in. Enable using --enable-gtk-doc. - -GObject and Introspection: -- Added harfbuzz-gobject library (hb-gobject.h) that has type - bindings for all HarfBuzz objects and enums. Enable using - --with-gobject. -- Added gobject-introspection boilerplate. Nothing useful - right now. Work in progress. Gets enabled automatically if - --with-gobject is used. Override with --disable-introspection. - -OpenType shaper: -- Apply 'mark' in Myanmar shaper. -- Don't apply 'dlig' by default. - -Uniscribe shaper: -- Support user features. -- Fix loading of fonts that are also installed on the system. -- Fix shaping of Arabic Presentation Forms. -- Fix build with wide chars. - -CoreText shaper: -- Support user features. - -Source changes: -- hb_face_t code moved to hb-face.h / hb-face.cc. -- Added hb-deprecated.h. - -API changes: -- Added HB_DISABLE_DEPRECATED. -- Deprecated HB_SCRIPT_CANADIAN_ABORIGINAL; replaced by - HB_SCRIPT_CANADIAN_SYLLABICS. -- Deprecated HB_BUFFER_FLAGS_DEFAULT; replaced by - HB_BUFFER_FLAG_DEFAULT. -- Deprecated HB_BUFFER_SERIALIZE_FLAGS_DEFAULT; replaced by - HB_BUFFER_SERIALIZE_FLAG_DEFAULT. - - -Overview of changes leading to 0.9.19 -Tuesday, July 16, 2013 -===================================== - -- Build fixes. -- Better handling of multiple variation selectors in a row. -- Pass on variation selector to GSUB if not consumed by cmap. -- Fix undefined memory access. -- Add Javanese config to Indic shaper. -- Misc bug fixes. - -Overview of changes leading to 0.9.18 -Tuesday, May 28, 2013 -===================================== - -New build system: - -- All unneeded code is all disabled by default, - -- Uniscribe and CoreText shapers can be enabled with their --with options, - -- icu_le and old shapers cannot be enabled for now, - -- glib, freetype, and cairo will be detected automatically. - They can be force on/off'ed with their --with options, - -- icu and graphite2 are default off, can be enabled with their --with - options, - -Moreover, ICU support is now build into a separate library: -libharfbuzz-icu.so, and a new harfbuzz-icu.pc is shipped for it. -Distros can enable ICU now without every application on earth -getting linked to via libharfbuzz.so. - -For distros I recommend that they make sure they are building --with-glib ---with-freetype --with-cairo, --with-icu, and optionally --with-graphite2; -And package harfbuzz and harfbuzz-icu separately. - - -Overview of changes leading to 0.9.17 -Monday, May 20, 2013 -===================================== - -- Build fixes. -- Fix bug in hb_set_get_min(). -- Fix regression with Arabic mark positioning / width-zeroing. - -Overview of changes leading to 0.9.16 -Friday, April 19, 2013 -===================================== - -- Major speedup in OpenType lookup processing. With the Amiri - Arabic font, this release is over 3x faster than previous - release. All scripts / languages should see this speedup. - -- New --num-iterations option for hb-shape / hb-view; useful for - profiling. - -Overview of changes leading to 0.9.15 -Friday, April 05, 2013 -===================================== - -- Build fixes. -- Fix crasher in graphite2 shaper. -- Fix Arabic mark width zeroing regression. -- Don't compose Hangul jamo into Unicode syllables. - - -Overview of changes leading to 0.9.14 -Thursday, March 21, 2013 -===================================== - -- Build fixes. -- Fix time-consuming sanitize with malicious fonts. -- Implement hb_buffer_deserialize_glyphs() for both json and text. -- Do not ignore Hangul filler characters. -- Indic fixes: - * Fix Malayalam pre-base reordering interaction with post-forms. - * Further adjust ZWJ handling. Should fix known regressions from - 0.9.13. - - -Overview of changes leading to 0.9.13 -Thursday, February 25, 2013 -===================================== - -- Build fixes. -- Ngapi HarfBuzz Hackfest in London (February 2013): - * Fixed all known Indic bugs, - * New Win8-style Myanmar shaper, - * New South-East Asian shaper for Tai Tham, Cham, and New Tai Lue, - * Smartly ignore Default_Ignorable characters (joiners, etc) wheb - matching GSUB/GPOS lookups, - * Fix 'Phags-Pa U+A872 shaping, - * Fix partial disabling of default-on features, - * Allow disabling of TrueType kerning. -- Fix possible crasher with broken fonts with overlapping tables. -- Removed generated files from git again. So, one needs ragel to - bootstrap from the git tree. - -API changes: -- hb_shape() and related APIs now abort if buffer direction is - HB_DIRECTION_INVALID. Previously, hb_shape() was calling - hb_buffer_guess_segment_properties() on the buffer before - shaping. The heuristics in that function are fragile. If the - user really wants the old behvaior, they can call that function - right before calling hb_shape() to get the old behavior. -- hb_blob_create_sub_blob() always creates sub-blob with - HB_MEMORY_MODE_READONLY. See comments for the reason. - - -Overview of changes leading to 0.9.12 -Thursday, January 18, 2013 -===================================== - -- Build fixes for Sun compiler. -- Minor bug fix. - -Overview of changes leading to 0.9.11 -Thursday, January 10, 2013 -===================================== - -- Build fixes. -- Fix GPOS mark attachment with null Anchor offsets. -- [Indic] Fix old-spec reordering of viramas if sequence ends in one. -- Fix multi-threaded shaper data creation crash. -- Add atomic ops for Solaris. - -API changes: -- Rename hb_buffer_clear() to hb_buffer_clear_contents(). - - -Overview of changes leading to 0.9.10 -Thursday, January 3, 2013 -===================================== - -- [Indic] Fixed rendering of Malayalam dot-reph -- Updated OT language tags. -- Updated graphite2 backend. -- Improved hb_ot_layout_get_size_params() logic. -- Improve hb-shape/hb-view help output. -- Fixed hb-set.h implementation to not crash. -- Fixed various issues with hb_ot_layout_collect_lookups(). -- Various build fixes. - -New API: - -hb_graphite2_face_get_gr_face() -hb_graphite2_font_get_gr_font() -hb_coretext_face_get_cg_font() - -Modified API: - -hb_ot_layout_get_size_params() - - -Overview of changes leading to 0.9.9 -Wednesday, December 5, 2012 -==================================== - -- Fix build on Windows. -- Minor improvements. - - -Overview of changes leading to 0.9.8 -Tuesday, December 4, 2012 -==================================== - - -- Actually implement hb_shape_plan_get_shaper (). -- Make UCDB data tables const. -- Lots of internal refactoring in OTLayout tables. -- Flesh out hb_ot_layout_lookup_collect_glyphs(). - -New API: - -hb_ot_layout_collect_lookups() -hb_ot_layout_get_size_params() - - -Overview of changes leading to 0.9.7 -Sunday, November 21, 2012 -==================================== - - -HarfBuzz "All-You-Can-Eat-Sushi" (aka Vancouver) Hackfest and follow-on fixes. - -- Fix Arabic contextual joining using pre-context text. -- Fix Sinhala "split matra" mess. -- Fix Khmer shaping with broken fonts. -- Implement Thai "PUA" shaping for old fonts. -- Do NOT route Kharoshthi script through the Indic shaper. -- Disable fallback positioning for Indic and Thai shapers. -- Misc fixes. - - -hb-shape / hb-view changes: - -- Add --text-before and --text-after -- Add --bot / --eot / --preserve-default-ignorables -- hb-shape --output-format=json - - -New API: - -hb_buffer_clear() - -hb_buffer_flags_t - -HB_BUFFER_FLAGS_DEFAULT -HB_BUFFER_FLAG_BOT -HB_BUFFER_FLAG_EOT -HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES - -hb_buffer_set_flags() -hb_buffer_get_flags() - -HB_BUFFER_SERIALIZE_FLAGS -hb_buffer_serialize_glyphs() -hb_buffer_deserialize_glyphs() -hb_buffer_serialize_list_formats() - -hb_set_add_range() -hb_set_del_range() -hb_set_get_population() -hb_set_next_range() - -hb_face_[sg]et_glyph_count() - -hb_segment_properties_t -HB_SEGMENT_PROPERTIES_DEFAULT -hb_segment_properties_equal() -hb_segment_properties_hash() - -hb_buffer_set_segment_properties() -hb_buffer_get_segment_properties() - -hb_ot_layout_glyph_class_t -hb_ot_layout_get_glyph_class() -hb_ot_layout_get_glyphs_in_class() - -hb_shape_plan_t -hb_shape_plan_create() -hb_shape_plan_create_cached() -hb_shape_plan_get_empty() -hb_shape_plan_reference() -hb_shape_plan_destroy() -hb_shape_plan_set_user_data() -hb_shape_plan_get_user_data() -hb_shape_plan_execute() -hb_shape_plan_get_shaper() - -hb_ot_shape_plan_collect_lookups() - - -API changes: - -- Remove "mask" parameter from hb_buffer_add(). -- Rename hb_ot_layout_would_substitute_lookup() and hb_ot_layout_substitute_closure_lookup(). -- hb-set.h API const correction. -- Renamed hb_set_min/max() to hb_set_get_min/max(). -- Rename hb_ot_layout_feature_get_lookup_indexes() to hb_ot_layout_feature_get_lookups(). -- Rename hb_buffer_guess_properties() to hb_buffer_guess_segment_properties(). - - - -Overview of changes leading to 0.9.6 -Sunday, November 13, 2012 -==================================== - -- Don't clear pre-context text if no new context is provided. -- Fix ReverseChainingSubstLookup, which was totally borked. -- Adjust output format of hb-shape a bit. -- Include config.h.in in-tree. Makes it easier for alternate build systems. -- Fix hb_buffer_set_length(buffer, 0) invalid memory allocation. -- Use ICU LayoutEngine's C API instead of C++. Avoids much headache. -- Drop glyphs for all of Unicode Default_Ignorable characters. -- Misc build fixes. - -Arabic shaper: -- Enable 'dlig' and 'mset' features in Arabic shaper. -- Implement 'Phags-pa shaping, improve Mongolian. - -Indic shaper: -- Decompose Sinhala split matras the way old HarfBuzz / Pango did. -- Initial support for Consonant Medials. -- Start adding new-style Myanmar shaping. -- Make reph and 'pref' logic introspect the font. -- Route Meetei-Mayek through the Indic shaper. -- Don't apply 'liga' in Indic shaper. -- Improve Malayalam pre-base reordering Ra interaction with Chillus. - - - -Overview of changes leading to 0.9.5 -Sunday, October 14, 2012 -==================================== - -- Synthetic-GSUB Arabic fallback shaping. - -- Misc Indic improvements. - -- Add build system support for pthread. - -- Imported UCDN for in-tree Unicode callbacks implementation. - -- Context-aware Arabic joining. - -- Misc other fixes. - -- New API: - - hb_feature_to/from-string() - hb_buffer_[sg]et_content_type() - - - -Overview of changes leading to 0.9.4 -Tuesday, Sep 03, 2012 -==================================== - -- Indic improvements with old-spec Malayalam. - -- Better fallback glyph positioning, specially with Thai / Lao marks. - -- Implement dotted-circle insertion. - -- Better Arabic fallback shaping / ligation. - -- Added ICU LayoutEngine backend for testing. Call it by the 'icu_le' name. - -- Misc fixes. - - - -Overview of changes leading to 0.9.3 -Friday, Aug 18, 2012 -==================================== - -- Fixed fallback mark positioning for left-to-right text. - -- Improve mark positioning for the remaining combining classes. - -- Unbreak Thai and fallback Arabic shaping. - -- Port Arabic shaper to shape-plan caching. - -- Use new ICU normalizer functions. - - - -Overview of changes leading to 0.9.2 -Friday, Aug 10, 2012 -==================================== - -- Over a thousand commits! This is the first major release of HarfBuzz. - -- HarfBuzz is feature-complete now! It should be in par, or better, than - both Pango's shapers and old HarfBuzz / Qt shapers. - -- New Indic shaper, supporting main Indic scripts, Sinhala, and Khmer. - -- Improved Arabic shaper, with fallback Arabic shaping, supporting Arabic, - Sinhala, N'ko, Mongolian, and Mandaic. - -- New Thai / Lao shaper. - -- Tibetan / Hangul support in the generic shaper. - -- Synthetic GDEF support for fonts without a GDEF table. - -- Fallback mark positioning for fonts without a GPOS table. - -- Unicode normalization shaping heuristic during glyph mapping. - -- New experimental Graphite2 backend. - -- New Uniscribe backend (primarily for testing). - -- New CoreText backend (primarily for testing). - -- Major optimization and speedup. - -- Test suites and testing infrastructure (work in progress). - -- Greatly improved hb-view cmdline tool. - -- hb-shape cmdline tool. - -- Unicode 6.1 support. - -Summary of API changes: - -o Changed API: - - - Users are expected to only include main header files now (ie. hb.h, - hb-glib.h, hb-ft.h, ...) - - - All struct tag names had their initial underscore removed. - Ie. "struct _hb_buffer_t" is "struct hb_buffer_t" now. - - - All set_user_data() functions now take a "replace" boolean parameter. - - - hb_buffer_create() takes zero arguments now. - Use hb_buffer_pre_allocate() to pre-allocate. - - - hb_buffer_add_utf*() now accept -1 for length parameteres, - meaning "nul-terminated". - - - hb_direction_t enum values changed. - - - All *_from_string() APIs now take a length parameter to allow for - non-nul-terminated strings. A -1 length means "nul-terminated". - - - Typedef for hb_language_t changed. - - - hb_get_table_func_t renamed to hb_reference_table_func_t. - - - hb_ot_layout_table_choose_script() - - - Various renames in hb-unicode.h. - -o New API: - - - hb_buffer_guess_properties() - Automatically called by hb_shape(). - - - hb_buffer_normalize_glyphs() - - - hb_tag_from_string() - - - hb-coretext.h - - - hb-uniscribe.h - - - hb_face_reference_blob() - - hb_face_[sg]et_index() - - hb_face_set_upem() - - - hb_font_get_glyph_name_func_t - hb_font_get_glyph_from_name_func_t - hb_font_funcs_set_glyph_name_func() - hb_font_funcs_set_glyph_from_name_func() - hb_font_get_glyph_name() - hb_font_get_glyph_from_name() - hb_font_glyph_to_string() - hb_font_glyph_from_string() - - - hb_font_set_funcs_data() - - - hb_ft_font_set_funcs() - - hb_ft_font_get_face() - - - hb-gobject.h (work in progress) - - - hb_ot_shape_glyphs_closure() - hb_ot_layout_substitute_closure_lookup() - - - hb-set.h - - - hb_shape_full() - - - hb_unicode_combining_class_t - - - hb_unicode_compose_func_t - hb_unicode_decompose_func_t - hb_unicode_decompose_compatibility_func_t - hb_unicode_funcs_set_compose_func() - hb_unicode_funcs_set_decompose_func() - hb_unicode_funcs_set_decompose_compatibility_func() - hb_unicode_compose() - hb_unicode_decompose() - hb_unicode_decompose_compatibility() - -o Removed API: - - - hb_ft_get_font_funcs() - - - hb_ot_layout_substitute_start() - hb_ot_layout_substitute_lookup() - hb_ot_layout_substitute_finish() - hb_ot_layout_position_start() - hb_ot_layout_position_lookup() - hb_ot_layout_position_finish() - - - -Overview of changes leading to 0.6.0 -Friday, May 27, 2011 -==================================== - -- Vertical text support in GPOS -- Almost all API entries have unit tests now, under test/ -- All thread-safety issues are fixed - -Summary of API changes follows. - - -* Simple Types API: - - o New API: - HB_LANGUAGE_INVALID - hb_language_get_default() - hb_direction_to_string() - hb_direction_from_string() - hb_script_get_horizontal_direction() - HB_UNTAG() - - o Renamed API: - hb_category_t renamed to hb_unicode_general_category_t - - o Changed API: - hb_language_t is a typed pointers now - - o Removed API: - HB_TAG_STR() - - -* Use ISO 15924 tags for hb_script_t: - - o New API: - hb_script_from_iso15924_tag() - hb_script_to_iso15924_tag() - hb_script_from_string() - - o Changed API: - HB_SCRIPT_* enum members changed value. - - -* Buffer API streamlined: - - o New API: - hb_buffer_reset() - hb_buffer_set_length() - hb_buffer_allocation_successful() - - o Renamed API: - hb_buffer_ensure() renamed to hb_buffer_pre_allocate() - hb_buffer_add_glyph() renamed to hb_buffer_add() - - o Removed API: - hb_buffer_clear() - hb_buffer_clear_positions() - - o Changed API: - hb_buffer_get_glyph_infos() takes an out length parameter now - hb_buffer_get_glyph_positions() takes an out length parameter now - - -* Blob API streamlined: - - o New API: - hb_blob_get_data() - hb_blob_get_data_writable() - - o Renamed API: - hb_blob_create_empty() renamed to hb_blob_get_empty() - - o Removed API: - hb_blob_lock() - hb_blob_unlock() - hb_blob_is_writable() - hb_blob_try_writable() - - o Changed API: - hb_blob_create() takes user_data before destroy now - - -* Unicode functions API: - - o Unicode function vectors can subclass other unicode function vectors now. - Unimplemented callbacks in the subclass automatically chainup to the parent. - - o All hb_unicode_funcs_t callbacks take a user_data now. Their setters - take a user_data and its respective destroy callback. - - o New API: - hb_unicode_funcs_get_empty() - hb_unicode_funcs_get_default() - hb_unicode_funcs_get_parent() - - o Changed API: - hb_unicode_funcs_create() now takes a parent_funcs. - - o Removed func getter functions: - hb_unicode_funcs_get_mirroring_func() - hb_unicode_funcs_get_general_category_func() - hb_unicode_funcs_get_script_func() - hb_unicode_funcs_get_combining_class_func() - hb_unicode_funcs_get_eastasian_width_func() - - -* Face API: - - o Renamed API: - hb_face_get_table() renamed to hb_face_reference_table() - hb_face_create_for_data() renamed to hb_face_create() - - o Changed API: - hb_face_create_for_tables() takes user_data before destroy now - hb_face_reference_table() returns empty blob instead of NULL - hb_get_table_func_t accepts the face as first parameter now - -* Font API: - - o Fonts can subclass other fonts now. Unimplemented callbacks in the - subclass automatically chainup to the parent. When chaining up, - scale is adjusted if the parent font has a different scale. - - o All hb_font_funcs_t callbacks take a user_data now. Their setters - take a user_data and its respective destroy callback. - - o New API: - hb_font_get_parent() - hb_font_funcs_get_empty() - hb_font_create_sub_font() - - o Removed API: - hb_font_funcs_copy() - hb_font_unset_funcs() - - o Removed func getter functions: - hb_font_funcs_get_glyph_func() - hb_font_funcs_get_glyph_advance_func() - hb_font_funcs_get_glyph_extents_func() - hb_font_funcs_get_contour_point_func() - hb_font_funcs_get_kerning_func() - - o Changed API: - hb_font_create() takes a face and references it now - hb_font_set_funcs() takes user_data before destroy now - hb_font_set_scale() accepts signed integers now - hb_font_get_contour_point_func_t now takes glyph first, then point_index - hb_font_get_glyph_func_t returns a success boolean now - - -* Changed object model: - - o All object types have a _get_empty() now: - hb_blob_get_empty() - hb_buffer_get_empty() - hb_face_get_empty() - hb_font_get_empty() - hb_font_funcs_get_empty() - hb_unicode_funcs_get_empty() - - o Added _set_user_data() and _get_user_data() for all object types: - hb_blob_get_user_data() - hb_blob_set_user_data() - hb_buffer_get_user_data() - hb_buffer_set_user_data() - hb_face_get_user_data() - hb_face_set_user_data() - hb_font_funcs_get_user_data() - hb_font_funcs_set_user_data() - hb_font_get_user_data() - hb_font_set_user_data() - hb_unicode_funcs_get_user_data() - hb_unicode_funcs_set_user_data() - - o Removed the _get_reference_count() from all object types: - hb_blob_get_reference_count() - hb_buffer_get_reference_count() - hb_face_get_reference_count() - hb_font_funcs_get_reference_count() - hb_font_get_reference_count() - hb_unicode_funcs_get_reference_count() - - o Added _make_immutable() and _is_immutable() for all object types except for buffer: - hb_blob_make_immutable() - hb_blob_is_immutable() - hb_face_make_immutable() - hb_face_is_immutable() - - -* Changed API for vertical text support - - o The following callbacks where removed: - hb_font_get_glyph_advance_func_t - hb_font_get_kerning_func_t - - o The following new callbacks added instead: - hb_font_get_glyph_h_advance_func_t - hb_font_get_glyph_v_advance_func_t - hb_font_get_glyph_h_origin_func_t - hb_font_get_glyph_v_origin_func_t - hb_font_get_glyph_h_kerning_func_t - hb_font_get_glyph_v_kerning_func_t - - o The following API removed as such: - hb_font_funcs_set_glyph_advance_func() - hb_font_funcs_set_kerning_func() - hb_font_get_glyph_advance() - hb_font_get_kerning() - - o New API added instead: - hb_font_funcs_set_glyph_h_advance_func() - hb_font_funcs_set_glyph_v_advance_func() - hb_font_funcs_set_glyph_h_origin_func() - hb_font_funcs_set_glyph_v_origin_func() - hb_font_funcs_set_glyph_h_kerning_func() - hb_font_funcs_set_glyph_v_kerning_func() - hb_font_get_glyph_h_advance() - hb_font_get_glyph_v_advance() - hb_font_get_glyph_h_origin() - hb_font_get_glyph_v_origin() - hb_font_get_glyph_h_kerning() - hb_font_get_glyph_v_kerning() - - o The following higher-leve API added for convenience: - hb_font_get_glyph_advance_for_direction() - hb_font_get_glyph_origin_for_direction() - hb_font_add_glyph_origin_for_direction() - hb_font_subtract_glyph_origin_for_direction() - hb_font_get_glyph_kerning_for_direction() - hb_font_get_glyph_extents_for_origin() - hb_font_get_glyph_contour_point_for_origin() - - -* OpenType Layout API: - - o New API: - hb_ot_layout_position_start() - hb_ot_layout_substitute_start() - hb_ot_layout_substitute_finish() - - -* Glue code: - - o New API: - hb_glib_script_to_script() - hb_glib_script_from_script() - hb_icu_script_to_script() - hb_icu_script_from_script() - - -* Version API added: - - o New API: - HB_VERSION_MAJOR - HB_VERSION_MINOR - HB_VERSION_MICRO - HB_VERSION_STRING - HB_VERSION_CHECK() - hb_version() - hb_version_string() - hb_version_check() - - diff --git a/src/3rdparty/harfbuzz-ng/README b/src/3rdparty/harfbuzz-ng/README deleted file mode 100644 index aa055169d5a..00000000000 --- a/src/3rdparty/harfbuzz-ng/README +++ /dev/null @@ -1,15 +0,0 @@ -[![Build Status](https://travis-ci.org/harfbuzz/harfbuzz.svg)](https://travis-ci.org/harfbuzz/harfbuzz) -[![Build status](https://ci.appveyor.com/api/projects/status/0t0flrxpstj9lb9w?svg=true)](https://ci.appveyor.com/project/harfbuzz/harfbuzz) -[![CircleCI](https://circleci.com/gh/harfbuzz/harfbuzz.svg?style=svg)](https://circleci.com/gh/harfbuzz/harfbuzz) -[![Coverage Status](https://img.shields.io/coveralls/harfbuzz/harfbuzz.svg)](https://coveralls.io/r/harfbuzz/harfbuzz) -[ABI Tracker](http://abi-laboratory.pro/tracker/timeline/harfbuzz/) - -This is HarfBuzz, a text shaping library. - -For bug reports, mailing list, and other information please visit: - - http://harfbuzz.org/ - -For license information, see the file COPYING. - -Documentation: https://harfbuzz.github.io diff --git a/src/3rdparty/harfbuzz-ng/README.md b/src/3rdparty/harfbuzz-ng/README.md new file mode 100644 index 00000000000..099d4b7719c --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/README.md @@ -0,0 +1,102 @@ +[![Linux CI Status](https://github.com/harfbuzz/harfbuzz/workflows/linux-ci/badge.svg)](https://github.com/harfbuzz/harfbuzz/workflows/linux-ci/badge.svg) +[![CircleCI Build Status](https://circleci.com/gh/harfbuzz/harfbuzz/tree/main.svg?style=svg)](https://circleci.com/gh/harfbuzz/harfbuzz/tree/main) +[![OSS-Fuzz Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/harfbuzz.svg)](https://oss-fuzz-build-logs.storage.googleapis.com/index.html) +[![Coverity Scan Build Status](https://scan.coverity.com/projects/15166/badge.svg)](https://scan.coverity.com/projects/harfbuzz) +[![Codacy Badge](https://app.codacy.com/project/badge/Grade/89c872f5ce1c42af802602bfcd15d90a)](https://www.codacy.com/gh/harfbuzz/harfbuzz/dashboard?utm_source=github.com&utm_medium=referral&utm_content=harfbuzz/harfbuzz&utm_campaign=Badge_Grade) +[![Codecov Code Coverage](https://codecov.io/gh/harfbuzz/harfbuzz/branch/main/graph/badge.svg)](https://codecov.io/gh/harfbuzz/harfbuzz) +[![Packaging status](https://repology.org/badge/tiny-repos/harfbuzz.svg)](https://repology.org/project/harfbuzz/versions) +[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/harfbuzz/harfbuzz/badge)](https://securityscorecards.dev/viewer/?uri=github.com/harfbuzz/harfbuzz) + + +# HarfBuzz + +HarfBuzz is a text shaping engine. It primarily supports [OpenType][1], but also +[Apple Advanced Typography][2]. HarfBuzz is used in Android, Chrome, +ChromeOS, Firefox, GNOME, GTK+, KDE, Qt, LibreOffice, OpenJDK, XeTeX, +PlayStation, Microsoft Edge, Photoshop, Illustrator, InDesign, +and other places. + +For bug reports, mailing list, and other information please visit: + + http://harfbuzz.org/ + +For license information, see [COPYING](COPYING). + +## Documentation + +For user manual as well as API documentation, check: https://harfbuzz.github.io + +## Download + +For tarball releases of HarfBuzz, look [here][3]. At the same place you +will also find Win32/Win64 binary bundles that include `libharfbuzz` DLL, +`hb-view.exe`, `hb-shape.exe`, and all dependencies. + +The canonical source tree is available on [github][4]. + +The API that comes with `hb.h` will not change incompatibly. Other, peripheral, +headers are more likely to go through minor modifications, but again, we do our +best to never change API in an incompatible way. We will never break the ABI. + +If you are not sure whether Pango or HarfBuzz is right for you, read [Pango vs +HarfBuzz][5]. + +## Development + +For build information, see [BUILD.md](BUILD.md). + +For custom configurations, see [CONFIG.md](CONFIG.md). + +For testing and profiling, see [TESTING.md](TESTING.md). + +To get a better idea of where HarfBuzz stands in the text rendering stack you +may want to read [State of Text Rendering][6], though, that document is many +years old. Here are a few presentation slides about HarfBuzz at the +Internationalization and Unicode Conference over the years: + +* November 2014, [Unicode, OpenType, and HarfBuzz: Closing the Circle][7], +* October 2012, [HarfBuzz, The Free and Open Text Shaping Engine][8], +* October 2009, [HarfBuzz: the Free and Open Shaping Engine][9]. + +Both development and user support discussion around HarfBuzz happens on the +[github][4]. + +To report bugs or submit patches please use [github][4] issues and +pull-requests. + +For a comparison of old vs new HarfBuzz memory consumption see [this][10]. + + + +## Name + +HarfBuzz (حرف‌باز) is my Persian translation of “[OpenType][1]”, +transliterated using the Latin script. It sports a second meaning, but that +ain’t translatable. + +> Background: Originally there was this font format called TrueType. People and +> companies started calling their type engines all things ending in Type: +> FreeType, CoolType, ClearType, etc. And then came OpenType, which is the +> successor of TrueType. So, for my OpenType implementation, I decided to stick +> with the concept but use the Persian translation. Which is fitting given that +> Persian is written in the Arabic script, and OpenType is an extension of +> TrueType that adds support for complex script rendering, and HarfBuzz is an +> implementation of OpenType complex text shaping. + +
+ Packaging status of HarfBuzz + +[![Packaging status](https://repology.org/badge/vertical-allrepos/harfbuzz.svg?header=harfbuzz)](https://repology.org/project/harfbuzz/versions) + +
+ +[1]: https://docs.microsoft.com/en-us/typography/opentype/spec/ +[2]: https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6AATIntro.html +[3]: https://github.com/harfbuzz/harfbuzz/releases +[4]: https://github.com/harfbuzz/harfbuzz +[5]: http://mces.blogspot.com/2009/11/pango-vs-harfbuzz.html +[6]: http://behdad.org/text/ +[7]: https://goo.gl/FSIQuC +[8]: https://goo.gl/2wSRu +[9]: http://behdad.org/download/Presentations/slippy/harfbuzz_slides.pdf +[10]: https://goo.gl/woyty diff --git a/src/3rdparty/harfbuzz-ng/THANKS b/src/3rdparty/harfbuzz-ng/THANKS index 940cfde5c39..88cb7e9ea12 100644 --- a/src/3rdparty/harfbuzz-ng/THANKS +++ b/src/3rdparty/harfbuzz-ng/THANKS @@ -1,6 +1,6 @@ Bradley Grainger -Khaled Hosny Kenichi Ishibashi +Ivan Kuckir Ryan Lortie Jeff Muizelaar suzuki toshiya diff --git a/src/3rdparty/harfbuzz-ng/config.h b/src/3rdparty/harfbuzz-ng/config.h index b8b6b3c0fe3..38087a71ef4 100644 --- a/src/3rdparty/harfbuzz-ng/config.h +++ b/src/3rdparty/harfbuzz-ng/config.h @@ -22,6 +22,9 @@ * */ +#ifndef QHARFBUZZ_CONFIG_H +#define QHARFBUZZ_CONFIG_H + #include QT_USE_NAMESPACE @@ -37,11 +40,19 @@ inline QAtomicPointer *makeAtomicPointer(T * const &ptr) return reinterpret_cast *>(const_cast(&ptr)); } +static inline void _hb_memory_barrier () +{ + QAtomicInt a; + a.ref(); // Ordered memory semantics, so imposes a memory barrier at this point +} + } // namespace typedef int hb_atomic_int_impl_t; #define HB_ATOMIC_INT_IMPL_INIT(V) (V) -#define hb_atomic_int_impl_add(AI, V) reinterpret_cast(AI).fetchAndAddOrdered(V) +#define hb_atomic_int_impl_add(AI, V) reinterpret_cast(AI)->fetchAndAddOrdered(V) #define hb_atomic_ptr_impl_get(P) makeAtomicPointer(*(P))->loadAcquire() #define hb_atomic_ptr_impl_cmpexch(P,O,N) makeAtomicPointer(*(P))->testAndSetOrdered((O), (N)) + +#endif // QHARFBUZZ_CONFIG_H diff --git a/src/3rdparty/harfbuzz-ng/harfbuzz-ng.pro b/src/3rdparty/harfbuzz-ng/harfbuzz-ng.pro index 19699389321..d9f843ccdba 100644 --- a/src/3rdparty/harfbuzz-ng/harfbuzz-ng.pro +++ b/src/3rdparty/harfbuzz-ng/harfbuzz-ng.pro @@ -14,11 +14,11 @@ SHAPERS += opentype # HB's main shaper; enabling it should be enough most # native shaper on Apple platforms; could be used alone to handle both OT and AAT fonts darwin: SHAPERS += coretext -# fallback shaper: not really useful with opentype or coretext shaper -#SHAPERS += fallback +# fallback shaper: not really useful with opentype or coretext shaper but needed for linking +SHAPERS += fallback DEFINES += HAVE_CONFIG_H -DEFINES += HB_NO_UNICODE_FUNCS HB_DISABLE_DEPRECATED +DEFINES += HB_NO_UNICODE_FUNCS DEFINES += HB_NDEBUG DEFINES += HB_EXTERN= @@ -26,6 +26,7 @@ DEFINES += HB_EXTERN= DEFINES += HAVE_ATEXIT unix: DEFINES += HAVE_PTHREAD HAVE_SCHED_H HAVE_SCHED_YIELD win32: DEFINES += HB_NO_WIN1256 +msvc:winrt: QMAKE_CXXFLAGS += /bigobj # prevent error C1128 #Workaround https://code.google.com/p/android/issues/detail?id=194631 android: DEFINES += _POSIX_C_SOURCE=200112L @@ -35,35 +36,46 @@ INCLUDEPATH += $$QT.core.includes DEFINES += QT_NO_VERSION_TAGGING SOURCES += \ + $$PWD/hb-dummy.cc \ + $$PWD/src/hb-aat-layout.cc \ + $$PWD/src/hb-aat-map.cc \ $$PWD/src/hb-blob.cc \ $$PWD/src/hb-buffer.cc \ $$PWD/src/hb-buffer-serialize.cc \ $$PWD/src/hb-common.cc \ + $$PWD/src/hb-draw.cc \ $$PWD/src/hb-face.cc \ + $$PWD/src/hb-face-builder.cc \ $$PWD/src/hb-font.cc \ + $$PWD/src/hb-map.cc \ + $$PWD/src/hb-number.cc \ $$PWD/src/hb-ot-tag.cc \ + $$PWD/src/hb-outline.cc \ + $$PWD/src/hb-paint.cc \ + $$PWD/src/hb-paint-extents.cc \ $$PWD/src/hb-set.cc \ $$PWD/src/hb-shape.cc \ $$PWD/src/hb-shape-plan.cc \ $$PWD/src/hb-shaper.cc \ + $$PWD/src/hb-style.cc \ + $$PWD/src/hb-subset.cc \ + $$PWD/src/hb-subset-cff-common.cc \ + $$PWD/src/hb-subset-cff1.cc \ + $$PWD/src/hb-subset-cff2.cc \ + $$PWD/src/hb-subset-input.cc \ + $$PWD/src/hb-subset-instancer-solver.cc \ + $$PWD/src/hb-subset-plan.cc \ + $$PWD/src/hb-subset-repacker.cc \ $$PWD/src/hb-unicode.cc \ - $$PWD/src/hb-warning.cc + $$PWD/src/hb-buffer-verify.cc HEADERS += \ - $$PWD/src/hb-atomic-private.hh \ - $$PWD/src/hb-buffer-private.hh \ $$PWD/src/hb-buffer-deserialize-json.hh \ - $$PWD/src/hb-buffer-deserialize-text.hh \ - $$PWD/src/hb-cache-private.hh \ + $$PWD/src/hb-buffer-deserialize-text-glyphs.hh \ + $$PWD/src/hb-buffer-deserialize-text-unicode.hh \ $$PWD/src/hb-debug.hh \ - $$PWD/src/hb-dsalgs.hh \ - $$PWD/src/hb-face-private.hh \ - $$PWD/src/hb-font-private.hh \ - $$PWD/src/hb-mutex-private.hh \ - $$PWD/src/hb-object-private.hh \ - $$PWD/src/hb-open-file-private.hh \ - $$PWD/src/hb-open-type-private.hh \ - $$PWD/src/hb-ot-cbdt-table.hh \ + $$PWD/src/hb-face.hh \ + $$PWD/src/hb-mutex.hh \ $$PWD/src/hb-ot-cmap-table.hh \ $$PWD/src/hb-ot-glyf-table.hh \ $$PWD/src/hb-ot-head-table.hh \ @@ -73,83 +85,120 @@ HEADERS += \ $$PWD/src/hb-ot-name-table.hh \ $$PWD/src/hb-ot-os2-table.hh \ $$PWD/src/hb-ot-post-table.hh \ - $$PWD/src/hb-private.hh \ - $$PWD/src/hb-set-digest-private.hh \ - $$PWD/src/hb-set-private.hh \ - $$PWD/src/hb-shape-plan-private.hh \ - $$PWD/src/hb-shaper-impl-private.hh \ + $$PWD/src/hb-set.hh \ + $$PWD/src/hb-set-digest.hh \ + $$PWD/src/hb-shape-plan.hh \ + $$PWD/src/hb-shaper.hh \ + $$PWD/src/hb-shaper-impl.hh \ $$PWD/src/hb-shaper-list.hh \ - $$PWD/src/hb-shaper-private.hh \ $$PWD/src/hb-string-array.hh \ - $$PWD/src/hb-unicode-private.hh \ - $$PWD/src/hb-utf-private.hh + $$PWD/src/hb-subset-plan-member-list.hh \ + $$PWD/src/hb-subset-repacker.h \ + $$PWD/src/hb-unicode.hh HEADERS += \ $$PWD/src/hb.h \ + $$PWD/src/hb-algs.hh \ + $$PWD/src/hb-atomic.hh \ $$PWD/src/hb-blob.h \ $$PWD/src/hb-buffer.h \ + $$PWD/src/hb-buffer.hh \ + $$PWD/src/hb-cache.hh \ $$PWD/src/hb-common.h \ $$PWD/src/hb-deprecated.h \ + $$PWD/src/hb-draw.h \ + $$PWD/src/hb-draw.hh \ $$PWD/src/hb-face.h \ $$PWD/src/hb-font.h \ + $$PWD/src/hb-font.hh \ + $$PWD/src/hb-ft-colr.hh \ + $$PWD/src/hb-limits.hh \ + $$PWD/src/hb-map.h \ + $$PWD/src/hb-map.hh \ + $$PWD/src/hb-object.hh \ + $$PWD/src/hb-open-file.hh \ + $$PWD/src/hb-open-type.hh \ + $$PWD/src/hb-ot-cff1-std-str.hh \ + $$PWD/src/hb-outline.hh \ + $$PWD/src/hb-paint.h \ + $$PWD/src/hb-paint.hh \ + $$PWD/src/hb-paint-extents.hh \ + $$PWD/src/hb-priority-queue.hh \ + $$PWD/src/hb-repacker.hh \ $$PWD/src/hb-set.h \ $$PWD/src/hb-shape.h \ $$PWD/src/hb-shape-plan.h \ + $$PWD/src/hb-style.h \ $$PWD/src/hb-unicode.h \ + $$PWD/src/hb-utf.hh \ $$PWD/src/hb-version.h contains(SHAPERS, opentype) { DEFINES += HAVE_OT SOURCES += \ + $$PWD/src/hb-ot-cff1-table.cc \ + $$PWD/src/hb-ot-cff2-table.cc \ + $$PWD/src/hb-ot-color.cc \ + $$PWD/src/hb-ot-face.cc \ $$PWD/src/hb-ot-font.cc \ $$PWD/src/hb-ot-layout.cc \ $$PWD/src/hb-ot-map.cc \ $$PWD/src/hb-ot-math.cc \ + $$PWD/src/hb-ot-meta.cc \ + $$PWD/src/hb-ot-metrics.cc \ + $$PWD/src/hb-ot-name.cc \ $$PWD/src/hb-ot-shape.cc \ - $$PWD/src/hb-ot-shape-complex-arabic.cc \ - $$PWD/src/hb-ot-shape-complex-default.cc \ - $$PWD/src/hb-ot-shape-complex-hangul.cc \ - $$PWD/src/hb-ot-shape-complex-hebrew.cc \ - $$PWD/src/hb-ot-shape-complex-indic.cc \ - $$PWD/src/hb-ot-shape-complex-indic-table.cc \ - $$PWD/src/hb-ot-shape-complex-myanmar.cc \ - $$PWD/src/hb-ot-shape-complex-thai.cc \ - $$PWD/src/hb-ot-shape-complex-tibetan.cc \ - $$PWD/src/hb-ot-shape-complex-use.cc \ - $$PWD/src/hb-ot-shape-complex-use-table.cc \ + $$PWD/src/hb-ot-shaper-arabic.cc \ + $$PWD/src/hb-ot-shaper-default.cc \ + $$PWD/src/hb-ot-shaper-hangul.cc \ + $$PWD/src/hb-ot-shaper-hebrew.cc \ + $$PWD/src/hb-ot-shaper-indic.cc \ + $$PWD/src/hb-ot-shaper-thai.cc \ $$PWD/src/hb-ot-shape-fallback.cc \ $$PWD/src/hb-ot-shape-normalize.cc \ - $$PWD/src/hb-ot-var.cc + $$PWD/src/hb-ot-shaper-indic-table.cc \ + $$PWD/src/hb-ot-shaper-khmer.cc \ + $$PWD/src/hb-ot-shaper-myanmar.cc \ + $$PWD/src/hb-ot-shaper-syllabic.cc \ + $$PWD/src/hb-ot-shaper-use.cc \ + $$PWD/src/hb-ot-shaper-vowel-constraints.cc \ + $$PWD/src/hb-ot-var.cc HEADERS += \ $$PWD/src/hb-ot-kern-table.hh \ - $$PWD/src/hb-ot-layout-common-private.hh \ $$PWD/src/hb-ot-layout-gdef-table.hh \ $$PWD/src/hb-ot-layout-gpos-table.hh \ - $$PWD/src/hb-ot-layout-gsubgpos-private.hh \ $$PWD/src/hb-ot-layout-gsub-table.hh \ $$PWD/src/hb-ot-layout-jstf-table.hh \ - $$PWD/src/hb-ot-layout-math-table.hh \ - $$PWD/src/hb-ot-layout-private.hh \ - $$PWD/src/hb-ot-map-private.hh \ + $$PWD/src/hb-ot-layout.hh \ + $$PWD/src/hb-ot-map.hh \ $$PWD/src/hb-ot-math-table.hh \ $$PWD/src/hb-ot-post-macroman.hh \ - $$PWD/src/hb-ot-shape-complex-arabic-fallback.hh \ - $$PWD/src/hb-ot-shape-complex-arabic-private.hh \ - $$PWD/src/hb-ot-shape-complex-arabic-table.hh \ -# $$PWD/src/hb-ot-shape-complex-arabic-win1256.hh \ # disabled with HB_NO_WIN1256 - $$PWD/src/hb-ot-shape-complex-indic-machine.hh \ - $$PWD/src/hb-ot-shape-complex-indic-private.hh \ - $$PWD/src/hb-ot-shape-complex-myanmar-machine.hh \ - $$PWD/src/hb-ot-shape-complex-private.hh \ - $$PWD/src/hb-ot-shape-complex-use-machine.hh \ - $$PWD/src/hb-ot-shape-complex-use-private.hh \ - $$PWD/src/hb-ot-shape-fallback-private.hh \ - $$PWD/src/hb-ot-shape-normalize-private.hh \ - $$PWD/src/hb-ot-shape-private.hh \ + $$PWD/src/hb-ot-post-table-v2subset.hh \ + $$PWD/src/hb-ot-shaper-arabic-joining-list.hh \ + $$PWD/src/hb-ot-shaper-arabic-pua.hh \ + $$PWD/src/hb-ot-shaper-indic-machine.hh \ + $$PWD/src/hb-ot-shaper-arabic-fallback.hh \ + $$PWD/src/hb-ot-shaper-arabic.hh \ + $$PWD/src/hb-ot-shaper-arabic-table.hh \ +# $$PWD/src/hb-ot-shaper-arabic-win1256.hh \ # disabled with HB_NO_WIN1256 + $$PWD/src/hb-ot-shaper.hh \ + $$PWD/src/hb-ot-shape-fallback.hh \ + $$PWD/src/hb-ot-shape-normalize.hh \ + $$PWD/src/hb-ot-shaper-indic.hh \ + $$PWD/src/hb-ot-shaper-khmer-machine.hh \ + $$PWD/src/hb-ot-shaper-myanmar-machine.hh \ + $$PWD/src/hb-ot-shaper-syllabic.hh \ + $$PWD/src/hb-ot-shaper-use-machine.hh \ + $$PWD/src/hb-ot-shaper-use-table.hh \ + $$PWD/src/hb-ot-shaper-vowel-constraints.hh \ + $$PWD/src/hb-subset-accelerator.hh \ + $$PWD/src/hb-ot-var-common.hh \ $$PWD/src/hb-ot-var-avar-table.hh \ + $$PWD/src/hb-ot-var-cvar-table.hh \ $$PWD/src/hb-ot-var-fvar-table.hh \ + $$PWD/src/hb-ot-var-gvar-table.hh \ $$PWD/src/hb-ot-var-hvar-table.hh \ $$PWD/src/hb-ot-var-mvar-table.hh @@ -159,8 +208,17 @@ contains(SHAPERS, opentype) { $$PWD/src/hb-ot-layout.h \ $$PWD/src/hb-ot-math.h \ $$PWD/src/hb-ot-shape.h \ - $$PWD/src/hb-ot-tag.h \ - $$PWD/src/hb-ot-var.h + $$PWD/src/hb-ot-shape.hh \ + $$PWD/src/hb-ot-var.h \ + $$PWD/src/OT/Color/CBDT/CBDT.hh \ + $$PWD/src/OT/Color/COLR/COLR.hh \ + $$PWD/src/OT/Color/COLR/colrv1-closure.hh \ + $$PWD/src/OT/Color/CPAL/CPAL.hh \ + $$PWD/src/OT/Color/sbix/sbix.hh \ + $$PWD/src/OT/Color/svg/svg.hh \ + $$PWD/src/OT/Layout/GDEF/GDEF.hh \ + $$PWD/src/OT/name/name.hh + } MODULE_EXT_HEADERS = $$HEADERS @@ -169,7 +227,7 @@ contains(SHAPERS, coretext) { DEFINES += HAVE_CORETEXT SOURCES += \ - $$PWD/src/hb-coretext.mm + $$PWD/src/hb-coretext.cc HEADERS += \ $$PWD/src/hb-coretext.h diff --git a/src/3rdparty/harfbuzz-ng/hb-dummy.cc b/src/3rdparty/harfbuzz-ng/hb-dummy.cc new file mode 100644 index 00000000000..490b8ae35c8 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/hb-dummy.cc @@ -0,0 +1,8 @@ +// Work-around for issue with qmake: Since hb-common.cc has #include "hb-static.cc" in it, +// qmake will assume it is included and ignore it in the SOURCES list. But the #include +// is protected inside an #ifdef and will not be used, so hb-static.cc ends up not being +// linked at all and we get missing symbols. We work around this by including both in +// the same compilation unit. + +#include "src/hb-common.cc" +#include "src/hb-static.cc" diff --git a/src/3rdparty/harfbuzz-ng/import_from_tarball.sh b/src/3rdparty/harfbuzz-ng/import_from_tarball.sh new file mode 100644 index 00000000000..5521ccf61bb --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/import_from_tarball.sh @@ -0,0 +1,92 @@ +#! /bin/sh + +############################################################################# +## +## Copyright (C) 2021 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is the build configuration utility of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:LGPL$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 3 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL3 included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 3 requirements +## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 2.0 or (at your option) the GNU General +## Public license version 3 or any later version approved by the KDE Free +## Qt Foundation. The licenses are as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-2.0.html and +## https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +# This is a small script to copy the required files from a harfbuzz tarball +# into 3rdparty/harfbuzz-ng/ . Documentation, tests, demos etc. are not imported. +# Steps: +# 1. rm $QTDIR/src/3rdparty/harfbuzz-ng/src/* +# 2. source import_from_tarball.sh harfbuzz_tarball_dir/ $QTDIR/src/3rdparty/harfbuzz-ng/ +# 3. Check that CMakeLists contains everything + +if [ $# -ne 2 ]; then + echo "Usage: $0 harfbuzz_tarball_dir/ \$QTDIR/src/3rdparty/harfbuzz-ng/" + exit 1 +fi + +HB_DIR=$1 +TARGET_DIR=$2 + +if [ ! -d "$HB_DIR" -o ! -r "$HB_DIR" -o ! -d "$TARGET_DIR" -o ! -w "$TARGET_DIR" ]; then + echo "Either the harfbuzz source dir or the target dir do not exist," + echo "are not directories or have the wrong permissions." + exit 2 +fi + +# with 1 argument, copies HB_DIR/$1 to TARGET_DIR/$1 +# with 2 arguments, copies HB_DIR/$1 to TARGET_DIR/$2 +copy_file_or_dir() { + if [ $# -lt 1 -o $# -gt 2 ]; then + echo "Wrong number of arguments to copy_file_or_dir" + exit 3 + fi + + SOURCE_FILE=$1 + if [ -n "$2" ]; then + DEST_FILE=$2 + else + DEST_FILE=$1 + fi + + mkdir -p "$TARGET_DIR/$(dirname "$SOURCE_FILE")" + cp -R "$HB_DIR/$SOURCE_FILE" "$TARGET_DIR/$DEST_FILE" +} + +FILES=(AUTHORS + COPYING + README.md + THANKS + ) + +for i in ${FILES[*]}; do + copy_file_or_dir "$i" +done + +cp -R $HB_DIR/src $TARGET_DIR diff --git a/src/3rdparty/harfbuzz-ng/qt_attribution.json b/src/3rdparty/harfbuzz-ng/qt_attribution.json index 2f1aed8a877..ab283a6f7db 100644 --- a/src/3rdparty/harfbuzz-ng/qt_attribution.json +++ b/src/3rdparty/harfbuzz-ng/qt_attribution.json @@ -3,24 +3,29 @@ "Name": "HarfBuzz-NG", "QDocModule": "qtgui", "QtUsage": "Optionally used in Qt GUI. Configure with -system-harfbuzz to force the use of the system library, or -qt-harfbuzz to link statically to the library that is bundled with your Qt version.", + "SecurityCritical": true, "Description": "HarfBuzz is an OpenType text shaping engine.", "Homepage": "http://harfbuzz.org", - "Version": "1.7.4", - + "Version": "8.2.2", "License": "MIT License", "LicenseId": "MIT", "LicenseFile": "COPYING", - "Copyright": "Copyright © 2010,2011,2012 Google, Inc. -Copyright © 2012 Mozilla Foundation -Copyright © 2011 Codethink Limited -Copyright © 2008,2010 Nokia Corporation and/or its subsidiary(-ies) -Copyright © 2009 Keith Stribley -Copyright © 2009 Martin Hosken and SIL International -Copyright © 2007 Chris Wilson -Copyright © 2006 Behdad Esfahbod -Copyright © 2005 David Turner -Copyright © 2004,2007,2008,2009,2010 Red Hat, Inc. -Copyright © 1998-2004 David Turner and Werner Lemberg -" + "Copyright": "Copyright © 2010-2022 Google, Inc. + Copyright © 2015-2020 Ebrahim Byagowi + Copyright © 2019,2020 Facebook, Inc. + Copyright © 2012,2015 Mozilla Foundation + Copyright © 2011 Codethink Limited + Copyright © 2008,2010 Nokia Corporation and/or its subsidiary(-ies) + Copyright © 2009 Keith Stribley + Copyright © 2011 Martin Hosken and SIL International + Copyright © 2007 Chris Wilson + Copyright © 2005,2006,2020,2021,2022,2023 Behdad Esfahbod + Copyright © 2004,2007,2008,2009,2010,2013,2021,2022,2023 Red Hat, Inc. + Copyright © 1998-2005 David Turner and Werner Lemberg + Copyright © 2016 Igalia S.L. + Copyright © 2022 Matthias Clasen + Copyright © 2018,2021 Khaled Hosny + Copyright © 2018,2019,2020 Adobe, Inc + Copyright © 2013-2015 Alexei Podtelezhnikov" } diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Color/CBDT/CBDT.hh b/src/3rdparty/harfbuzz-ng/src/OT/Color/CBDT/CBDT.hh new file mode 100644 index 00000000000..457039bfc6c --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Color/CBDT/CBDT.hh @@ -0,0 +1,1025 @@ +/* + * Copyright © 2016 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): Seigo Nonaka, Calder Kitagawa + */ + +#ifndef OT_COLOR_CBDT_CBDT_HH +#define OT_COLOR_CBDT_CBDT_HH + +#include "../../../hb-open-type.hh" +#include "../../../hb-paint.hh" + +/* + * CBLC -- Color Bitmap Location + * https://docs.microsoft.com/en-us/typography/opentype/spec/cblc + * https://docs.microsoft.com/en-us/typography/opentype/spec/eblc + * CBDT -- Color Bitmap Data + * https://docs.microsoft.com/en-us/typography/opentype/spec/cbdt + * https://docs.microsoft.com/en-us/typography/opentype/spec/ebdt + */ +#define HB_OT_TAG_CBLC HB_TAG('C','B','L','C') +#define HB_OT_TAG_CBDT HB_TAG('C','B','D','T') + + +namespace OT { + +struct cblc_bitmap_size_subset_context_t +{ + const char *cbdt; + unsigned int cbdt_length; + hb_vector_t *cbdt_prime; + unsigned int size; /* INOUT + * Input: old size of IndexSubtable + * Output: new size of IndexSubtable + */ + unsigned int num_tables; /* INOUT + * Input: old number of subtables. + * Output: new number of subtables. + */ + hb_codepoint_t start_glyph; /* OUT */ + hb_codepoint_t end_glyph; /* OUT */ +}; + +static inline bool +_copy_data_to_cbdt (hb_vector_t *cbdt_prime, + const void *data, + unsigned length) +{ + unsigned int new_len = cbdt_prime->length + length; + if (unlikely (!cbdt_prime->alloc (new_len))) return false; + hb_memcpy (cbdt_prime->arrayZ + cbdt_prime->length, data, length); + cbdt_prime->length = new_len; + return true; +} + +struct SmallGlyphMetrics +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + void get_extents (hb_font_t *font, hb_glyph_extents_t *extents, bool scale) const + { + extents->x_bearing = bearingX; + extents->y_bearing = bearingY; + extents->width = width; + extents->height = -static_cast (height); + + if (scale) + font->scale_glyph_extents (extents); + } + + HBUINT8 height; + HBUINT8 width; + HBINT8 bearingX; + HBINT8 bearingY; + HBUINT8 advance; + public: + DEFINE_SIZE_STATIC (5); +}; + +struct BigGlyphMetrics : SmallGlyphMetrics +{ + HBINT8 vertBearingX; + HBINT8 vertBearingY; + HBUINT8 vertAdvance; + public: + DEFINE_SIZE_STATIC (8); +}; + +struct SBitLineMetrics +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + HBINT8 ascender; + HBINT8 decender; + HBUINT8 widthMax; + HBINT8 caretSlopeNumerator; + HBINT8 caretSlopeDenominator; + HBINT8 caretOffset; + HBINT8 minOriginSB; + HBINT8 minAdvanceSB; + HBINT8 maxBeforeBL; + HBINT8 minAfterBL; + HBINT8 padding1; + HBINT8 padding2; + public: + DEFINE_SIZE_STATIC (12); +}; + + +/* + * Index Subtables. + */ + +struct IndexSubtableHeader +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + HBUINT16 indexFormat; + HBUINT16 imageFormat; + HBUINT32 imageDataOffset; + public: + DEFINE_SIZE_STATIC (8); +}; + +template +struct IndexSubtableFormat1Or3 +{ + bool sanitize (hb_sanitize_context_t *c, unsigned int glyph_count) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + offsetArrayZ.sanitize (c, glyph_count + 1)); + } + + bool get_image_data (unsigned int idx, + unsigned int *offset, + unsigned int *length) const + { + if (unlikely (offsetArrayZ[idx + 1] <= offsetArrayZ[idx])) + return false; + + *offset = header.imageDataOffset + offsetArrayZ[idx]; + *length = offsetArrayZ[idx + 1] - offsetArrayZ[idx]; + return true; + } + + bool add_offset (hb_serialize_context_t *c, + unsigned int offset, + unsigned int *size /* OUT (accumulated) */) + { + TRACE_SERIALIZE (this); + Offset embedded_offset; + embedded_offset = offset; + *size += sizeof (OffsetType); + auto *o = c->embed (embedded_offset); + return_trace ((bool) o); + } + + IndexSubtableHeader header; + UnsizedArrayOf> + offsetArrayZ; + public: + DEFINE_SIZE_ARRAY (8, offsetArrayZ); +}; + +struct IndexSubtableFormat1 : IndexSubtableFormat1Or3 {}; +struct IndexSubtableFormat3 : IndexSubtableFormat1Or3 {}; + +struct IndexSubtable +{ + bool sanitize (hb_sanitize_context_t *c, unsigned int glyph_count) const + { + TRACE_SANITIZE (this); + if (!u.header.sanitize (c)) return_trace (false); + switch (u.header.indexFormat) + { + case 1: return_trace (u.format1.sanitize (c, glyph_count)); + case 3: return_trace (u.format3.sanitize (c, glyph_count)); + default:return_trace (true); + } + } + + bool + finish_subtable (hb_serialize_context_t *c, + unsigned int cbdt_prime_len, + unsigned int num_glyphs, + unsigned int *size /* OUT (accumulated) */) + { + TRACE_SERIALIZE (this); + + unsigned int local_offset = cbdt_prime_len - u.header.imageDataOffset; + switch (u.header.indexFormat) + { + case 1: return_trace (u.format1.add_offset (c, local_offset, size)); + case 3: { + if (!u.format3.add_offset (c, local_offset, size)) + return_trace (false); + if (!(num_glyphs & 0x01)) // Pad to 32-bit alignment if needed. + return_trace (u.format3.add_offset (c, 0, size)); + return_trace (true); + } + // TODO: implement 2, 4, 5. + case 2: case 4: // No-op. + case 5: // Pad to 32-bit aligned. + default: return_trace (false); + } + } + + bool + fill_missing_glyphs (hb_serialize_context_t *c, + unsigned int cbdt_prime_len, + unsigned int num_missing, + unsigned int *size /* OUT (accumulated) */, + unsigned int *num_glyphs /* OUT (accumulated) */) + { + TRACE_SERIALIZE (this); + + unsigned int local_offset = cbdt_prime_len - u.header.imageDataOffset; + switch (u.header.indexFormat) + { + case 1: { + for (unsigned int i = 0; i < num_missing; i++) + { + if (unlikely (!u.format1.add_offset (c, local_offset, size))) + return_trace (false); + *num_glyphs += 1; + } + return_trace (true); + } + case 3: { + for (unsigned int i = 0; i < num_missing; i++) + { + if (unlikely (!u.format3.add_offset (c, local_offset, size))) + return_trace (false); + *num_glyphs += 1; + } + return_trace (true); + } + // TODO: implement 2, 4, 5. + case 2: // Add empty space in cbdt_prime?. + case 4: case 5: // No-op as sparse is supported. + default: return_trace (false); + } + } + + bool + copy_glyph_at_idx (hb_serialize_context_t *c, unsigned int idx, + const char *cbdt, unsigned int cbdt_length, + hb_vector_t *cbdt_prime /* INOUT */, + IndexSubtable *subtable_prime /* INOUT */, + unsigned int *size /* OUT (accumulated) */) const + { + TRACE_SERIALIZE (this); + + unsigned int offset, length, format; + if (unlikely (!get_image_data (idx, &offset, &length, &format))) return_trace (false); + if (unlikely (offset > cbdt_length || cbdt_length - offset < length)) return_trace (false); + + auto *header_prime = subtable_prime->get_header (); + unsigned int new_local_offset = cbdt_prime->length - (unsigned int) header_prime->imageDataOffset; + if (unlikely (!_copy_data_to_cbdt (cbdt_prime, cbdt + offset, length))) return_trace (false); + + return_trace (subtable_prime->add_offset (c, new_local_offset, size)); + } + + bool + add_offset (hb_serialize_context_t *c, unsigned int local_offset, + unsigned int *size /* OUT (accumulated) */) + { + TRACE_SERIALIZE (this); + switch (u.header.indexFormat) + { + case 1: return_trace (u.format1.add_offset (c, local_offset, size)); + case 3: return_trace (u.format3.add_offset (c, local_offset, size)); + // TODO: Implement tables 2, 4, 5 + case 2: // Should be a no-op. + case 4: case 5: // Handle sparse cases. + default: return_trace (false); + } + } + + bool get_extents (hb_glyph_extents_t *extents HB_UNUSED, bool scale HB_UNUSED) const + { + switch (u.header.indexFormat) + { + case 2: case 5: /* TODO */ + case 1: case 3: case 4: /* Variable-metrics formats do not have metrics here. */ + default:return (false); + } + } + + bool + get_image_data (unsigned int idx, unsigned int *offset, + unsigned int *length, unsigned int *format) const + { + *format = u.header.imageFormat; + switch (u.header.indexFormat) + { + case 1: return u.format1.get_image_data (idx, offset, length); + case 3: return u.format3.get_image_data (idx, offset, length); + default: return false; + } + } + + const IndexSubtableHeader* get_header () const { return &u.header; } + + void populate_header (unsigned index_format, + unsigned image_format, + unsigned int image_data_offset, + unsigned int *size) + { + u.header.indexFormat = index_format; + u.header.imageFormat = image_format; + u.header.imageDataOffset = image_data_offset; + switch (u.header.indexFormat) + { + case 1: *size += IndexSubtableFormat1::min_size; break; + case 3: *size += IndexSubtableFormat3::min_size; break; + } + } + + protected: + union { + IndexSubtableHeader header; + IndexSubtableFormat1 format1; + IndexSubtableFormat3 format3; + /* TODO: Format 2, 4, 5. */ + } u; + public: + DEFINE_SIZE_UNION (8, header); +}; + +struct IndexSubtableRecord +{ + /* XXX Remove this and fix by not inserting it into vector. */ + IndexSubtableRecord& operator = (const IndexSubtableRecord &o) + { + firstGlyphIndex = o.firstGlyphIndex; + lastGlyphIndex = o.lastGlyphIndex; + offsetToSubtable = (unsigned) o.offsetToSubtable; + assert (offsetToSubtable.is_null ()); + return *this; + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + firstGlyphIndex <= lastGlyphIndex && + offsetToSubtable.sanitize (c, base, lastGlyphIndex - firstGlyphIndex + 1)); + } + + const IndexSubtable* get_subtable (const void *base) const + { + return &(base+offsetToSubtable); + } + + bool add_new_subtable (hb_subset_context_t* c, + cblc_bitmap_size_subset_context_t *bitmap_size_context, + IndexSubtableRecord *record, + const hb_vector_t> *lookup, /* IN */ + const void *base, + unsigned int *start /* INOUT */) const + { + TRACE_SERIALIZE (this); + + auto *subtable = c->serializer->start_embed (); + if (unlikely (!c->serializer->extend_min (subtable))) return_trace (false); + + auto *old_subtable = get_subtable (base); + auto *old_header = old_subtable->get_header (); + + subtable->populate_header (old_header->indexFormat, + old_header->imageFormat, + bitmap_size_context->cbdt_prime->length, + &bitmap_size_context->size); + + unsigned int num_glyphs = 0; + bool early_exit = false; + for (unsigned int i = *start; i < lookup->length; i++) + { + hb_codepoint_t new_gid = (*lookup)[i].first; + const IndexSubtableRecord *next_record = (*lookup)[i].second; + const IndexSubtable *next_subtable = next_record->get_subtable (base); + auto *next_header = next_subtable->get_header (); + if (next_header != old_header) + { + *start = i; + early_exit = true; + break; + } + unsigned int num_missing = record->add_glyph_for_subset (new_gid); + if (unlikely (!subtable->fill_missing_glyphs (c->serializer, + bitmap_size_context->cbdt_prime->length, + num_missing, + &bitmap_size_context->size, + &num_glyphs))) + return_trace (false); + + hb_codepoint_t old_gid = 0; + c->plan->old_gid_for_new_gid (new_gid, &old_gid); + if (old_gid < next_record->firstGlyphIndex) + return_trace (false); + + unsigned int old_idx = (unsigned int) old_gid - next_record->firstGlyphIndex; + if (unlikely (!next_subtable->copy_glyph_at_idx (c->serializer, + old_idx, + bitmap_size_context->cbdt, + bitmap_size_context->cbdt_length, + bitmap_size_context->cbdt_prime, + subtable, + &bitmap_size_context->size))) + return_trace (false); + num_glyphs += 1; + } + if (!early_exit) + *start = lookup->length; + if (unlikely (!subtable->finish_subtable (c->serializer, + bitmap_size_context->cbdt_prime->length, + num_glyphs, + &bitmap_size_context->size))) + return_trace (false); + return_trace (true); + } + + bool add_new_record (hb_subset_context_t *c, + cblc_bitmap_size_subset_context_t *bitmap_size_context, + const hb_vector_t> *lookup, /* IN */ + const void *base, + unsigned int *start, /* INOUT */ + hb_vector_t* records /* INOUT */) const + { + TRACE_SERIALIZE (this); + auto snap = c->serializer->snapshot (); + unsigned int old_size = bitmap_size_context->size; + unsigned int old_cbdt_prime_length = bitmap_size_context->cbdt_prime->length; + + // Set to invalid state to indicate filling glyphs is not yet started. + if (unlikely (!c->serializer->check_success (records->resize (records->length + 1)))) + return_trace (false); + + records->tail ().firstGlyphIndex = 1; + records->tail ().lastGlyphIndex = 0; + bitmap_size_context->size += IndexSubtableRecord::min_size; + + c->serializer->push (); + + if (unlikely (!add_new_subtable (c, bitmap_size_context, &(records->tail ()), lookup, base, start))) + { + c->serializer->pop_discard (); + c->serializer->revert (snap); + bitmap_size_context->cbdt_prime->shrink (old_cbdt_prime_length); + bitmap_size_context->size = old_size; + records->resize (records->length - 1); + return_trace (false); + } + + bitmap_size_context->num_tables += 1; + return_trace (true); + } + + unsigned int add_glyph_for_subset (hb_codepoint_t gid) + { + if (firstGlyphIndex > lastGlyphIndex) + { + firstGlyphIndex = gid; + lastGlyphIndex = gid; + return 0; + } + // TODO maybe assert? this shouldn't occur. + if (lastGlyphIndex > gid) + return 0; + unsigned int num_missing = (unsigned int) (gid - lastGlyphIndex - 1); + lastGlyphIndex = gid; + return num_missing; + } + + bool get_extents (hb_glyph_extents_t *extents, const void *base, bool scale) const + { return (base+offsetToSubtable).get_extents (extents, scale); } + + bool get_image_data (unsigned int gid, + const void *base, + unsigned int *offset, + unsigned int *length, + unsigned int *format) const + { + if (gid < firstGlyphIndex || gid > lastGlyphIndex) return false; + return (base+offsetToSubtable).get_image_data (gid - firstGlyphIndex, + offset, length, format); + } + + HBGlyphID16 firstGlyphIndex; + HBGlyphID16 lastGlyphIndex; + Offset32To offsetToSubtable; + public: + DEFINE_SIZE_STATIC (8); +}; + +struct IndexSubtableArray +{ + friend struct CBDT; + + bool sanitize (hb_sanitize_context_t *c, unsigned int count) const + { + TRACE_SANITIZE (this); + return_trace (indexSubtablesZ.sanitize (c, count, this)); + } + + void + build_lookup (hb_subset_context_t *c, cblc_bitmap_size_subset_context_t *bitmap_size_context, + hb_vector_t> *lookup /* OUT */) const + { + bool start_glyph_is_set = false; + unsigned num_glyphs = c->plan->num_output_glyphs (); + for (hb_codepoint_t new_gid = 0; new_gid < num_glyphs; new_gid++) + { + hb_codepoint_t old_gid; + if (unlikely (!c->plan->old_gid_for_new_gid (new_gid, &old_gid))) continue; + + const IndexSubtableRecord* record = find_table (old_gid, bitmap_size_context->num_tables); + if (unlikely (!record)) continue; + + // Don't add gaps to the lookup. The best way to determine if a glyph is a + // gap is that it has no image data. + unsigned int offset, length, format; + if (unlikely (!record->get_image_data (old_gid, this, &offset, &length, &format))) continue; + + lookup->push (hb_pair_t (new_gid, record)); + + if (!start_glyph_is_set) + { + bitmap_size_context->start_glyph = new_gid; + start_glyph_is_set = true; + } + + bitmap_size_context->end_glyph = new_gid; + } + } + + bool + subset (hb_subset_context_t *c, + cblc_bitmap_size_subset_context_t *bitmap_size_context) const + { + TRACE_SUBSET (this); + + hb_vector_t> lookup; + build_lookup (c, bitmap_size_context, &lookup); + if (unlikely (!c->serializer->propagate_error (lookup))) + return false; + + bitmap_size_context->size = 0; + bitmap_size_context->num_tables = 0; + hb_vector_t records; + for (unsigned int start = 0; start < lookup.length;) + { + if (unlikely (!lookup[start].second->add_new_record (c, bitmap_size_context, &lookup, this, &start, &records))) + { + // Discard any leftover pushes to the serializer from successful records. + for (unsigned int i = 0; i < records.length; i++) + c->serializer->pop_discard (); + return_trace (false); + } + } + + /* Workaround to ensure offset ordering is from least to greatest when + * resolving links. */ + hb_vector_t objidxs; + for (unsigned int i = 0; i < records.length; i++) + objidxs.push (c->serializer->pop_pack ()); + for (unsigned int i = 0; i < records.length; i++) + { + IndexSubtableRecord* record = c->serializer->embed (records[i]); + if (unlikely (!record)) return_trace (false); + c->serializer->add_link (record->offsetToSubtable, objidxs[records.length - 1 - i]); + } + return_trace (true); + } + + public: + const IndexSubtableRecord* find_table (hb_codepoint_t glyph, unsigned int numTables) const + { + for (unsigned int i = 0; i < numTables; ++i) + { + unsigned int firstGlyphIndex = indexSubtablesZ[i].firstGlyphIndex; + unsigned int lastGlyphIndex = indexSubtablesZ[i].lastGlyphIndex; + if (firstGlyphIndex <= glyph && glyph <= lastGlyphIndex) + return &indexSubtablesZ[i]; + } + return nullptr; + } + + protected: + UnsizedArrayOf indexSubtablesZ; +}; + +struct BitmapSizeTable +{ + friend struct CBLC; + friend struct CBDT; + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + indexSubtableArrayOffset.sanitize (c, base, numberOfIndexSubtables) && + horizontal.sanitize (c) && + vertical.sanitize (c)); + } + + const IndexSubtableRecord * + find_table (hb_codepoint_t glyph, const void *base, const void **out_base) const + { + *out_base = &(base+indexSubtableArrayOffset); + return (base+indexSubtableArrayOffset).find_table (glyph, numberOfIndexSubtables); + } + + bool + subset (hb_subset_context_t *c, const void *base, + const char *cbdt, unsigned int cbdt_length, + hb_vector_t *cbdt_prime /* INOUT */) const + { + TRACE_SUBSET (this); + auto *out_table = c->serializer->embed (this); + if (unlikely (!out_table)) return_trace (false); + + cblc_bitmap_size_subset_context_t bitmap_size_context; + bitmap_size_context.cbdt = cbdt; + bitmap_size_context.cbdt_length = cbdt_length; + bitmap_size_context.cbdt_prime = cbdt_prime; + bitmap_size_context.size = indexTablesSize; + bitmap_size_context.num_tables = numberOfIndexSubtables; + bitmap_size_context.start_glyph = 1; + bitmap_size_context.end_glyph = 0; + + if (!out_table->indexSubtableArrayOffset.serialize_subset (c, + indexSubtableArrayOffset, + base, + &bitmap_size_context)) + return_trace (false); + if (!bitmap_size_context.size || + !bitmap_size_context.num_tables || + bitmap_size_context.start_glyph > bitmap_size_context.end_glyph) + return_trace (false); + + out_table->indexTablesSize = bitmap_size_context.size; + out_table->numberOfIndexSubtables = bitmap_size_context.num_tables; + out_table->startGlyphIndex = bitmap_size_context.start_glyph; + out_table->endGlyphIndex = bitmap_size_context.end_glyph; + return_trace (true); + } + + protected: + NNOffset32To + indexSubtableArrayOffset; + HBUINT32 indexTablesSize; + HBUINT32 numberOfIndexSubtables; + HBUINT32 colorRef; + SBitLineMetrics horizontal; + SBitLineMetrics vertical; + HBGlyphID16 startGlyphIndex; + HBGlyphID16 endGlyphIndex; + HBUINT8 ppemX; + HBUINT8 ppemY; + HBUINT8 bitDepth; + HBINT8 flags; + public: + DEFINE_SIZE_STATIC (48); +}; + + +/* + * Glyph Bitmap Data Formats. + */ + +struct GlyphBitmapDataFormat17 +{ + SmallGlyphMetrics glyphMetrics; + Array32Of data; + public: + DEFINE_SIZE_ARRAY (9, data); +}; + +struct GlyphBitmapDataFormat18 +{ + BigGlyphMetrics glyphMetrics; + Array32Of data; + public: + DEFINE_SIZE_ARRAY (12, data); +}; + +struct GlyphBitmapDataFormat19 +{ + Array32Of data; + public: + DEFINE_SIZE_ARRAY (4, data); +}; + +struct CBLC +{ + friend struct CBDT; + + static constexpr hb_tag_t tableTag = HB_OT_TAG_CBLC; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + likely (version.major == 2 || version.major == 3) && + sizeTables.sanitize (c, this)); + } + + static bool + sink_cbdt (hb_subset_context_t *c, hb_vector_t* cbdt_prime) + { + hb_blob_t *cbdt_prime_blob = hb_blob_create (cbdt_prime->arrayZ, + cbdt_prime->length, + HB_MEMORY_MODE_WRITABLE, + cbdt_prime->arrayZ, + hb_free); + cbdt_prime->init (); // Leak arrayZ to the blob. + bool ret = c->plan->add_table (HB_OT_TAG_CBDT, cbdt_prime_blob); + hb_blob_destroy (cbdt_prime_blob); + return ret; + } + + bool + subset_size_table (hb_subset_context_t *c, const BitmapSizeTable& table, + const char *cbdt /* IN */, unsigned int cbdt_length, + CBLC *cblc_prime /* INOUT */, hb_vector_t *cbdt_prime /* INOUT */) const + { + TRACE_SUBSET (this); + cblc_prime->sizeTables.len++; + + auto snap = c->serializer->snapshot (); + auto cbdt_prime_len = cbdt_prime->length; + + if (!table.subset (c, this, cbdt, cbdt_length, cbdt_prime)) + { + cblc_prime->sizeTables.len--; + c->serializer->revert (snap); + cbdt_prime->shrink (cbdt_prime_len); + return_trace (false); + } + return_trace (true); + } + + // Implemented in cc file as it depends on definition of CBDT. + HB_INTERNAL bool subset (hb_subset_context_t *c) const; + + protected: + const BitmapSizeTable &choose_strike (hb_font_t *font) const + { + unsigned count = sizeTables.len; + if (unlikely (!count)) + return Null (BitmapSizeTable); + + unsigned int requested_ppem = hb_max (font->x_ppem, font->y_ppem); + if (!requested_ppem) + requested_ppem = 1<<30; /* Choose largest strike. */ + unsigned int best_i = 0; + unsigned int best_ppem = hb_max (sizeTables[0].ppemX, sizeTables[0].ppemY); + + for (unsigned int i = 1; i < count; i++) + { + unsigned int ppem = hb_max (sizeTables[i].ppemX, sizeTables[i].ppemY); + if ((requested_ppem <= ppem && ppem < best_ppem) || + (requested_ppem > best_ppem && ppem > best_ppem)) + { + best_i = i; + best_ppem = ppem; + } + } + + return sizeTables[best_i]; + } + + protected: + FixedVersion<> version; + Array32Of sizeTables; + public: + DEFINE_SIZE_ARRAY (8, sizeTables); +}; + +struct CBDT +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_CBDT; + + struct accelerator_t + { + accelerator_t (hb_face_t *face) + { + this->cblc = hb_sanitize_context_t ().reference_table (face); + this->cbdt = hb_sanitize_context_t ().reference_table (face); + + upem = hb_face_get_upem (face); + } + ~accelerator_t () + { + this->cblc.destroy (); + this->cbdt.destroy (); + } + + bool + get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents, bool scale = true) const + { + const void *base; + const BitmapSizeTable &strike = this->cblc->choose_strike (font); + const IndexSubtableRecord *subtable_record = strike.find_table (glyph, cblc, &base); + if (!subtable_record || !strike.ppemX || !strike.ppemY) + return false; + + if (subtable_record->get_extents (extents, base, scale)) + return true; + + unsigned int image_offset = 0, image_length = 0, image_format = 0; + if (!subtable_record->get_image_data (glyph, base, &image_offset, &image_length, &image_format)) + return false; + + unsigned int cbdt_len = cbdt.get_length (); + if (unlikely (image_offset > cbdt_len || cbdt_len - image_offset < image_length)) + return false; + + switch (image_format) + { + case 17: { + if (unlikely (image_length < GlyphBitmapDataFormat17::min_size)) + return false; + auto &glyphFormat17 = StructAtOffset (this->cbdt, image_offset); + glyphFormat17.glyphMetrics.get_extents (font, extents, scale); + break; + } + case 18: { + if (unlikely (image_length < GlyphBitmapDataFormat18::min_size)) + return false; + auto &glyphFormat18 = StructAtOffset (this->cbdt, image_offset); + glyphFormat18.glyphMetrics.get_extents (font, extents, scale); + break; + } + default: return false; /* TODO: Support other image formats. */ + } + + /* Convert to font units. */ + if (scale) + { + float x_scale = upem / (float) strike.ppemX; + float y_scale = upem / (float) strike.ppemY; + extents->x_bearing = roundf (extents->x_bearing * x_scale); + extents->y_bearing = roundf (extents->y_bearing * y_scale); + extents->width = roundf (extents->width * x_scale); + extents->height = roundf (extents->height * y_scale); + } + + return true; + } + + hb_blob_t* + reference_png (hb_font_t *font, hb_codepoint_t glyph) const + { + const void *base; + const BitmapSizeTable &strike = this->cblc->choose_strike (font); + const IndexSubtableRecord *subtable_record = strike.find_table (glyph, cblc, &base); + if (!subtable_record || !strike.ppemX || !strike.ppemY) + return hb_blob_get_empty (); + + unsigned int image_offset = 0, image_length = 0, image_format = 0; + if (!subtable_record->get_image_data (glyph, base, &image_offset, &image_length, &image_format)) + return hb_blob_get_empty (); + + unsigned int cbdt_len = cbdt.get_length (); + if (unlikely (image_offset > cbdt_len || cbdt_len - image_offset < image_length)) + return hb_blob_get_empty (); + + switch (image_format) + { + case 17: + { + if (unlikely (image_length < GlyphBitmapDataFormat17::min_size)) + return hb_blob_get_empty (); + auto &glyphFormat17 = StructAtOffset (this->cbdt, image_offset); + return hb_blob_create_sub_blob (cbdt.get_blob (), + image_offset + GlyphBitmapDataFormat17::min_size, + glyphFormat17.data.len); + } + case 18: + { + if (unlikely (image_length < GlyphBitmapDataFormat18::min_size)) + return hb_blob_get_empty (); + auto &glyphFormat18 = StructAtOffset (this->cbdt, image_offset); + return hb_blob_create_sub_blob (cbdt.get_blob (), + image_offset + GlyphBitmapDataFormat18::min_size, + glyphFormat18.data.len); + } + case 19: + { + if (unlikely (image_length < GlyphBitmapDataFormat19::min_size)) + return hb_blob_get_empty (); + auto &glyphFormat19 = StructAtOffset (this->cbdt, image_offset); + return hb_blob_create_sub_blob (cbdt.get_blob (), + image_offset + GlyphBitmapDataFormat19::min_size, + glyphFormat19.data.len); + } + default: return hb_blob_get_empty (); /* TODO: Support other image formats. */ + } + } + + bool has_data () const { return cbdt.get_length (); } + + bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data) const + { + hb_glyph_extents_t extents; + hb_glyph_extents_t pixel_extents; + hb_blob_t *blob = reference_png (font, glyph); + + if (unlikely (blob == hb_blob_get_empty ())) + return false; + + if (unlikely (!hb_font_get_glyph_extents (font, glyph, &extents))) + return false; + + if (unlikely (!get_extents (font, glyph, &pixel_extents, false))) + return false; + + bool ret = funcs->image (data, + blob, + pixel_extents.width, -pixel_extents.height, + HB_PAINT_IMAGE_FORMAT_PNG, + font->slant_xy, + &extents); + + hb_blob_destroy (blob); + return ret; + } + + private: + hb_blob_ptr_t cblc; + hb_blob_ptr_t cbdt; + + unsigned int upem; + }; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + likely (version.major == 2 || version.major == 3)); + } + + protected: + FixedVersion<> version; + UnsizedArrayOf dataZ; + public: + DEFINE_SIZE_ARRAY (4, dataZ); +}; + +inline bool +CBLC::subset (hb_subset_context_t *c) const +{ + TRACE_SUBSET (this); + + // Use a vector as a secondary buffer as the tables need to be built in parallel. + hb_vector_t cbdt_prime; + + auto *cblc_prime = c->serializer->start_embed (); + if (unlikely (!c->serializer->extend_min (cblc_prime))) return_trace (false); + cblc_prime->version = version; + + hb_blob_t* cbdt_blob = hb_sanitize_context_t ().reference_table (c->plan->source); + unsigned int cbdt_length; + CBDT* cbdt = (CBDT *) hb_blob_get_data (cbdt_blob, &cbdt_length); + if (unlikely (cbdt_length < CBDT::min_size)) + { + hb_blob_destroy (cbdt_blob); + return_trace (false); + } + _copy_data_to_cbdt (&cbdt_prime, cbdt, CBDT::min_size); + + for (const BitmapSizeTable& table : + sizeTables.iter ()) + subset_size_table (c, table, (const char *) cbdt, cbdt_length, cblc_prime, &cbdt_prime); + + hb_blob_destroy (cbdt_blob); + + return_trace (CBLC::sink_cbdt (c, &cbdt_prime)); +} + +struct CBDT_accelerator_t : CBDT::accelerator_t { + CBDT_accelerator_t (hb_face_t *face) : CBDT::accelerator_t (face) {} +}; + + +} /* namespace OT */ + +#endif /* OT_COLOR_CBDT_CBDT_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Color/COLR/COLR.hh b/src/3rdparty/harfbuzz-ng/src/OT/Color/COLR/COLR.hh new file mode 100644 index 00000000000..60b094ecbc8 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Color/COLR/COLR.hh @@ -0,0 +1,2488 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * Copyright © 2020 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): Calder Kitagawa + */ + +#ifndef OT_COLOR_COLR_COLR_HH +#define OT_COLOR_COLR_COLR_HH + +#include "../../../hb.hh" +#include "../../../hb-open-type.hh" +#include "../../../hb-ot-var-common.hh" +#include "../../../hb-paint.hh" +#include "../../../hb-paint-extents.hh" + +/* + * COLR -- Color + * https://docs.microsoft.com/en-us/typography/opentype/spec/colr + */ +#define HB_OT_TAG_COLR HB_TAG('C','O','L','R') + +namespace OT { +struct hb_paint_context_t; +} + +namespace OT { + +struct COLR; + +struct Paint; + +struct hb_paint_context_t : + hb_dispatch_context_t +{ + const char *get_name () { return "PAINT"; } + template + return_t dispatch (const T &obj) { obj.paint_glyph (this); return hb_empty_t (); } + static return_t default_return_value () { return hb_empty_t (); } + + const COLR* get_colr_table () const + { return reinterpret_cast (base); } + +public: + const void *base; + hb_paint_funcs_t *funcs; + void *data; + hb_font_t *font; + unsigned int palette_index; + hb_color_t foreground; + VarStoreInstancer &instancer; + hb_map_t current_glyphs; + hb_map_t current_layers; + int depth_left = HB_MAX_NESTING_LEVEL; + int edge_count = HB_COLRV1_MAX_EDGE_COUNT; + + hb_paint_context_t (const void *base_, + hb_paint_funcs_t *funcs_, + void *data_, + hb_font_t *font_, + unsigned int palette_, + hb_color_t foreground_, + VarStoreInstancer &instancer_) : + base (base_), + funcs (funcs_), + data (data_), + font (font_), + palette_index (palette_), + foreground (foreground_), + instancer (instancer_) + { } + + hb_color_t get_color (unsigned int color_index, float alpha, hb_bool_t *is_foreground) + { + hb_color_t color = foreground; + + *is_foreground = true; + + if (color_index != 0xffff) + { + if (!funcs->custom_palette_color (data, color_index, &color)) + { + unsigned int clen = 1; + hb_face_t *face = hb_font_get_face (font); + + hb_ot_color_palette_get_colors (face, palette_index, color_index, &clen, &color); + } + + *is_foreground = false; + } + + return HB_COLOR (hb_color_get_blue (color), + hb_color_get_green (color), + hb_color_get_red (color), + hb_color_get_alpha (color) * alpha); + } + + inline void recurse (const Paint &paint); +}; + +struct hb_colrv1_closure_context_t : + hb_dispatch_context_t +{ + template + return_t dispatch (const T &obj) + { + if (unlikely (nesting_level_left == 0)) + return hb_empty_t (); + + if (paint_visited (&obj)) + return hb_empty_t (); + + nesting_level_left--; + obj.closurev1 (this); + nesting_level_left++; + return hb_empty_t (); + } + static return_t default_return_value () { return hb_empty_t (); } + + bool paint_visited (const void *paint) + { + hb_codepoint_t delta = (hb_codepoint_t) ((uintptr_t) paint - (uintptr_t) base); + if (visited_paint.in_error() || visited_paint.has (delta)) + return true; + + visited_paint.add (delta); + return false; + } + + const COLR* get_colr_table () const + { return reinterpret_cast (base); } + + void add_glyph (unsigned glyph_id) + { glyphs->add (glyph_id); } + + void add_layer_indices (unsigned first_layer_index, unsigned num_of_layers) + { layer_indices->add_range (first_layer_index, first_layer_index + num_of_layers - 1); } + + void add_palette_index (unsigned palette_index) + { palette_indices->add (palette_index); } + + public: + const void *base; + hb_set_t visited_paint; + hb_set_t *glyphs; + hb_set_t *layer_indices; + hb_set_t *palette_indices; + unsigned nesting_level_left; + + hb_colrv1_closure_context_t (const void *base_, + hb_set_t *glyphs_, + hb_set_t *layer_indices_, + hb_set_t *palette_indices_, + unsigned nesting_level_left_ = HB_MAX_NESTING_LEVEL) : + base (base_), + glyphs (glyphs_), + layer_indices (layer_indices_), + palette_indices (palette_indices_), + nesting_level_left (nesting_level_left_) + {} +}; + +struct LayerRecord +{ + operator hb_ot_color_layer_t () const { return {glyphId, colorIdx}; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + HBGlyphID16 glyphId; /* Glyph ID of layer glyph */ + Index colorIdx; /* Index value to use with a + * selected color palette. + * An index value of 0xFFFF + * is a special case indicating + * that the text foreground + * color (defined by a + * higher-level client) should + * be used and shall not be + * treated as actual index + * into CPAL ColorRecord array. */ + public: + DEFINE_SIZE_STATIC (4); +}; + +struct BaseGlyphRecord +{ + int cmp (hb_codepoint_t g) const + { return g < glyphId ? -1 : g > glyphId ? 1 : 0; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + HBGlyphID16 glyphId; /* Glyph ID of reference glyph */ + HBUINT16 firstLayerIdx; /* Index (from beginning of + * the Layer Records) to the + * layer record. There will be + * numLayers consecutive entries + * for this base glyph. */ + HBUINT16 numLayers; /* Number of color layers + * associated with this glyph */ + public: + DEFINE_SIZE_STATIC (6); +}; + +template +struct Variable +{ + static constexpr bool is_variable = true; + + Variable* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + return_trace (c->embed (this)); + } + + void closurev1 (hb_colrv1_closure_context_t* c) const + { value.closurev1 (c); } + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer) const + { + TRACE_SUBSET (this); + if (!value.subset (c, instancer, varIdxBase)) return_trace (false); + if (c->plan->all_axes_pinned) + return_trace (true); + + //TODO: update varIdxBase for partial-instancing + return_trace (c->serializer->embed (varIdxBase)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && value.sanitize (c)); + } + + void paint_glyph (hb_paint_context_t *c) const + { + TRACE_PAINT (this); + value.paint_glyph (c, varIdxBase); + } + + void get_color_stop (hb_paint_context_t *c, + hb_color_stop_t *stop, + const VarStoreInstancer &instancer) const + { + value.get_color_stop (c, stop, varIdxBase, instancer); + } + + hb_paint_extend_t get_extend () const + { + return value.get_extend (); + } + + protected: + T value; + public: + VarIdx varIdxBase; + public: + DEFINE_SIZE_MIN (VarIdx::static_size + T::min_size); +}; + +template +struct NoVariable +{ + static constexpr bool is_variable = false; + + static constexpr uint32_t varIdxBase = VarIdx::NO_VARIATION; + + NoVariable* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + return_trace (c->embed (this)); + } + + void closurev1 (hb_colrv1_closure_context_t* c) const + { value.closurev1 (c); } + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer) const + { + TRACE_SUBSET (this); + return_trace (value.subset (c, instancer, varIdxBase)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && value.sanitize (c)); + } + + void paint_glyph (hb_paint_context_t *c) const + { + TRACE_PAINT (this); + value.paint_glyph (c, varIdxBase); + } + + void get_color_stop (hb_paint_context_t *c, + hb_color_stop_t *stop, + const VarStoreInstancer &instancer) const + { + value.get_color_stop (c, stop, VarIdx::NO_VARIATION, instancer); + } + + hb_paint_extend_t get_extend () const + { + return value.get_extend (); + } + + T value; + public: + DEFINE_SIZE_MIN (T::min_size); +}; + +// Color structures + +struct ColorStop +{ + void closurev1 (hb_colrv1_closure_context_t* c) const + { c->add_palette_index (paletteIndex); } + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer, + uint32_t varIdxBase) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (*this); + if (unlikely (!out)) return_trace (false); + + if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION) + { + out->stopOffset.set_float (stopOffset.to_float(instancer (varIdxBase, 0))); + out->alpha.set_float (alpha.to_float (instancer (varIdxBase, 1))); + } + + return_trace (c->serializer->check_assign (out->paletteIndex, c->plan->colr_palettes.get (paletteIndex), + HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + void get_color_stop (hb_paint_context_t *c, + hb_color_stop_t *out, + uint32_t varIdx, + const VarStoreInstancer &instancer) const + { + out->offset = stopOffset.to_float(instancer (varIdx, 0)); + out->color = c->get_color (paletteIndex, + alpha.to_float (instancer (varIdx, 1)), + &out->is_foreground); + } + + F2DOT14 stopOffset; + HBUINT16 paletteIndex; + F2DOT14 alpha; + public: + DEFINE_SIZE_STATIC (2 + 2 * F2DOT14::static_size); +}; + +struct Extend : HBUINT8 +{ + enum { + EXTEND_PAD = 0, + EXTEND_REPEAT = 1, + EXTEND_REFLECT = 2, + }; + public: + DEFINE_SIZE_STATIC (1); +}; + +template class Var> +struct ColorLine +{ + void closurev1 (hb_colrv1_closure_context_t* c) const + { + for (const auto &stop : stops.iter ()) + stop.closurev1 (c); + } + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + if (!c->serializer->check_assign (out->extend, extend, HB_SERIALIZE_ERROR_INT_OVERFLOW)) return_trace (false); + if (!c->serializer->check_assign (out->stops.len, stops.len, HB_SERIALIZE_ERROR_ARRAY_OVERFLOW)) return_trace (false); + + for (const auto& stop : stops.iter ()) + { + if (!stop.subset (c, instancer)) return_trace (false); + } + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + stops.sanitize (c)); + } + + /* get up to count stops from start */ + unsigned int + get_color_stops (hb_paint_context_t *c, + unsigned int start, + unsigned int *count, + hb_color_stop_t *color_stops, + const VarStoreInstancer &instancer) const + { + unsigned int len = stops.len; + + if (count && color_stops) + { + unsigned int i; + for (i = 0; i < *count && start + i < len; i++) + stops[start + i].get_color_stop (c, &color_stops[i], instancer); + *count = i; + } + + return len; + } + + HB_INTERNAL static unsigned int static_get_color_stops (hb_color_line_t *color_line, + void *color_line_data, + unsigned int start, + unsigned int *count, + hb_color_stop_t *color_stops, + void *user_data) + { + const ColorLine *thiz = (const ColorLine *) color_line_data; + hb_paint_context_t *c = (hb_paint_context_t *) user_data; + return thiz->get_color_stops (c, start, count, color_stops, c->instancer); + } + + hb_paint_extend_t get_extend () const + { + return (hb_paint_extend_t) (unsigned int) extend; + } + + HB_INTERNAL static hb_paint_extend_t static_get_extend (hb_color_line_t *color_line, + void *color_line_data, + void *user_data) + { + const ColorLine *thiz = (const ColorLine *) color_line_data; + return thiz->get_extend (); + } + + Extend extend; + Array16Of> stops; + public: + DEFINE_SIZE_ARRAY_SIZED (3, stops); +}; + +// Composition modes + +// Compositing modes are taken from https://www.w3.org/TR/compositing-1/ +// NOTE: a brief audit of major implementations suggests most support most +// or all of the specified modes. +struct CompositeMode : HBUINT8 +{ + enum { + // Porter-Duff modes + // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators + COMPOSITE_CLEAR = 0, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_clear + COMPOSITE_SRC = 1, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_src + COMPOSITE_DEST = 2, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dst + COMPOSITE_SRC_OVER = 3, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_srcover + COMPOSITE_DEST_OVER = 4, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dstover + COMPOSITE_SRC_IN = 5, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_srcin + COMPOSITE_DEST_IN = 6, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dstin + COMPOSITE_SRC_OUT = 7, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_srcout + COMPOSITE_DEST_OUT = 8, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dstout + COMPOSITE_SRC_ATOP = 9, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_srcatop + COMPOSITE_DEST_ATOP = 10, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dstatop + COMPOSITE_XOR = 11, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_xor + COMPOSITE_PLUS = 12, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_plus + + // Blend modes + // https://www.w3.org/TR/compositing-1/#blending + COMPOSITE_SCREEN = 13, // https://www.w3.org/TR/compositing-1/#blendingscreen + COMPOSITE_OVERLAY = 14, // https://www.w3.org/TR/compositing-1/#blendingoverlay + COMPOSITE_DARKEN = 15, // https://www.w3.org/TR/compositing-1/#blendingdarken + COMPOSITE_LIGHTEN = 16, // https://www.w3.org/TR/compositing-1/#blendinglighten + COMPOSITE_COLOR_DODGE = 17, // https://www.w3.org/TR/compositing-1/#blendingcolordodge + COMPOSITE_COLOR_BURN = 18, // https://www.w3.org/TR/compositing-1/#blendingcolorburn + COMPOSITE_HARD_LIGHT = 19, // https://www.w3.org/TR/compositing-1/#blendinghardlight + COMPOSITE_SOFT_LIGHT = 20, // https://www.w3.org/TR/compositing-1/#blendingsoftlight + COMPOSITE_DIFFERENCE = 21, // https://www.w3.org/TR/compositing-1/#blendingdifference + COMPOSITE_EXCLUSION = 22, // https://www.w3.org/TR/compositing-1/#blendingexclusion + COMPOSITE_MULTIPLY = 23, // https://www.w3.org/TR/compositing-1/#blendingmultiply + + // Modes that, uniquely, do not operate on components + // https://www.w3.org/TR/compositing-1/#blendingnonseparable + COMPOSITE_HSL_HUE = 24, // https://www.w3.org/TR/compositing-1/#blendinghue + COMPOSITE_HSL_SATURATION = 25, // https://www.w3.org/TR/compositing-1/#blendingsaturation + COMPOSITE_HSL_COLOR = 26, // https://www.w3.org/TR/compositing-1/#blendingcolor + COMPOSITE_HSL_LUMINOSITY = 27, // https://www.w3.org/TR/compositing-1/#blendingluminosity + }; + public: + DEFINE_SIZE_STATIC (1); +}; + +struct Affine2x3 +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer, + uint32_t varIdxBase) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (*this); + if (unlikely (!out)) return_trace (false); + if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION) + { + out->xx.set_float (xx.to_float(instancer (varIdxBase, 0))); + out->yx.set_float (yx.to_float(instancer (varIdxBase, 1))); + out->xy.set_float (xy.to_float(instancer (varIdxBase, 2))); + out->yy.set_float (yy.to_float(instancer (varIdxBase, 3))); + out->dx.set_float (dx.to_float(instancer (varIdxBase, 4))); + out->dy.set_float (dy.to_float(instancer (varIdxBase, 5))); + } + return_trace (true); + } + + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + TRACE_PAINT (this); + c->funcs->push_transform (c->data, + xx.to_float (c->instancer (varIdxBase, 0)), + yx.to_float (c->instancer (varIdxBase, 1)), + xy.to_float (c->instancer (varIdxBase, 2)), + yy.to_float (c->instancer (varIdxBase, 3)), + dx.to_float (c->instancer (varIdxBase, 4)), + dy.to_float (c->instancer (varIdxBase, 5))); + } + + F16DOT16 xx; + F16DOT16 yx; + F16DOT16 xy; + F16DOT16 yy; + F16DOT16 dx; + F16DOT16 dy; + public: + DEFINE_SIZE_STATIC (6 * F16DOT16::static_size); +}; + +struct PaintColrLayers +{ + void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer HB_UNUSED) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + return_trace (c->serializer->check_assign (out->firstLayerIndex, c->plan->colrv1_layers.get (firstLayerIndex), + HB_SERIALIZE_ERROR_INT_OVERFLOW)); + + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + inline void paint_glyph (hb_paint_context_t *c) const; + + HBUINT8 format; /* format = 1 */ + HBUINT8 numLayers; + HBUINT32 firstLayerIndex; /* index into COLRv1::layerList */ + public: + DEFINE_SIZE_STATIC (6); +}; + +struct PaintSolid +{ + void closurev1 (hb_colrv1_closure_context_t* c) const + { c->add_palette_index (paletteIndex); } + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer, + uint32_t varIdxBase) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (*this); + if (unlikely (!out)) return_trace (false); + + if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION) + out->alpha.set_float (alpha.to_float (instancer (varIdxBase, 0))); + + if (format == 3 && c->plan->all_axes_pinned) + out->format = 2; + + return_trace (c->serializer->check_assign (out->paletteIndex, c->plan->colr_palettes.get (paletteIndex), + HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + TRACE_PAINT (this); + hb_bool_t is_foreground; + hb_color_t color; + + color = c->get_color (paletteIndex, + alpha.to_float (c->instancer (varIdxBase, 0)), + &is_foreground); + c->funcs->color (c->data, is_foreground, color); + } + + HBUINT8 format; /* format = 2(noVar) or 3(Var)*/ + HBUINT16 paletteIndex; + F2DOT14 alpha; + public: + DEFINE_SIZE_STATIC (3 + F2DOT14::static_size); +}; + +template class Var> +struct PaintLinearGradient +{ + void closurev1 (hb_colrv1_closure_context_t* c) const + { (this+colorLine).closurev1 (c); } + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer, + uint32_t varIdxBase) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION) + { + out->x0 = x0 + (int) roundf (instancer (varIdxBase, 0)); + out->y0 = y0 + (int) roundf (instancer (varIdxBase, 1)); + out->x1 = x1 + (int) roundf (instancer (varIdxBase, 2)); + out->y1 = y1 + (int) roundf (instancer (varIdxBase, 3)); + out->x2 = x2 + (int) roundf (instancer (varIdxBase, 4)); + out->y2 = y2 + (int) roundf (instancer (varIdxBase, 5)); + } + + if (format == 5 && c->plan->all_axes_pinned) + out->format = 4; + + return_trace (out->colorLine.serialize_subset (c, colorLine, this, instancer)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && colorLine.sanitize (c, this)); + } + + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + TRACE_PAINT (this); + hb_color_line_t cl = { + (void *) &(this+colorLine), + (this+colorLine).static_get_color_stops, c, + (this+colorLine).static_get_extend, nullptr + }; + + c->funcs->linear_gradient (c->data, &cl, + x0 + c->instancer (varIdxBase, 0), + y0 + c->instancer (varIdxBase, 1), + x1 + c->instancer (varIdxBase, 2), + y1 + c->instancer (varIdxBase, 3), + x2 + c->instancer (varIdxBase, 4), + y2 + c->instancer (varIdxBase, 5)); + } + + HBUINT8 format; /* format = 4(noVar) or 5 (Var) */ + Offset24To> colorLine; /* Offset (from beginning of PaintLinearGradient + * table) to ColorLine subtable. */ + FWORD x0; + FWORD y0; + FWORD x1; + FWORD y1; + FWORD x2; + FWORD y2; + public: + DEFINE_SIZE_STATIC (4 + 6 * FWORD::static_size); +}; + +template class Var> +struct PaintRadialGradient +{ + void closurev1 (hb_colrv1_closure_context_t* c) const + { (this+colorLine).closurev1 (c); } + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer, + uint32_t varIdxBase) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION) + { + out->x0 = x0 + (int) roundf (instancer (varIdxBase, 0)); + out->y0 = y0 + (int) roundf (instancer (varIdxBase, 1)); + out->radius0 = radius0 + (unsigned) roundf (instancer (varIdxBase, 2)); + out->x1 = x1 + (int) roundf (instancer (varIdxBase, 3)); + out->y1 = y1 + (int) roundf (instancer (varIdxBase, 4)); + out->radius1 = radius1 + (unsigned) roundf (instancer (varIdxBase, 5)); + } + + if (format == 7 && c->plan->all_axes_pinned) + out->format = 6; + + return_trace (out->colorLine.serialize_subset (c, colorLine, this, instancer)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && colorLine.sanitize (c, this)); + } + + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + TRACE_PAINT (this); + hb_color_line_t cl = { + (void *) &(this+colorLine), + (this+colorLine).static_get_color_stops, c, + (this+colorLine).static_get_extend, nullptr + }; + + c->funcs->radial_gradient (c->data, &cl, + x0 + c->instancer (varIdxBase, 0), + y0 + c->instancer (varIdxBase, 1), + radius0 + c->instancer (varIdxBase, 2), + x1 + c->instancer (varIdxBase, 3), + y1 + c->instancer (varIdxBase, 4), + radius1 + c->instancer (varIdxBase, 5)); + } + + HBUINT8 format; /* format = 6(noVar) or 7 (Var) */ + Offset24To> colorLine; /* Offset (from beginning of PaintRadialGradient + * table) to ColorLine subtable. */ + FWORD x0; + FWORD y0; + UFWORD radius0; + FWORD x1; + FWORD y1; + UFWORD radius1; + public: + DEFINE_SIZE_STATIC (4 + 6 * FWORD::static_size); +}; + +template class Var> +struct PaintSweepGradient +{ + void closurev1 (hb_colrv1_closure_context_t* c) const + { (this+colorLine).closurev1 (c); } + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer, + uint32_t varIdxBase) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION) + { + out->centerX = centerX + (int) roundf (instancer (varIdxBase, 0)); + out->centerY = centerY + (int) roundf (instancer (varIdxBase, 1)); + out->startAngle.set_float (startAngle.to_float (instancer (varIdxBase, 2))); + out->endAngle.set_float (endAngle.to_float (instancer (varIdxBase, 3))); + } + + if (format == 9 && c->plan->all_axes_pinned) + out->format = 8; + + return_trace (out->colorLine.serialize_subset (c, colorLine, this, instancer)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && colorLine.sanitize (c, this)); + } + + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + TRACE_PAINT (this); + hb_color_line_t cl = { + (void *) &(this+colorLine), + (this+colorLine).static_get_color_stops, c, + (this+colorLine).static_get_extend, nullptr + }; + + c->funcs->sweep_gradient (c->data, &cl, + centerX + c->instancer (varIdxBase, 0), + centerY + c->instancer (varIdxBase, 1), + (startAngle.to_float (c->instancer (varIdxBase, 2)) + 1) * HB_PI, + (endAngle.to_float (c->instancer (varIdxBase, 3)) + 1) * HB_PI); + } + + HBUINT8 format; /* format = 8(noVar) or 9 (Var) */ + Offset24To> colorLine; /* Offset (from beginning of PaintSweepGradient + * table) to ColorLine subtable. */ + FWORD centerX; + FWORD centerY; + F2DOT14 startAngle; + F2DOT14 endAngle; + public: + DEFINE_SIZE_STATIC (4 + 2 * FWORD::static_size + 2 * F2DOT14::static_size); +}; + +// Paint a non-COLR glyph, filled as indicated by paint. +struct PaintGlyph +{ + void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + if (! c->serializer->check_assign (out->gid, c->plan->glyph_map->get (gid), + HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); + + return_trace (out->paint.serialize_subset (c, paint, this, instancer)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && paint.sanitize (c, this)); + } + + void paint_glyph (hb_paint_context_t *c) const + { + TRACE_PAINT (this); + c->funcs->push_inverse_root_transform (c->data, c->font); + c->funcs->push_clip_glyph (c->data, gid, c->font); + c->funcs->push_root_transform (c->data, c->font); + c->recurse (this+paint); + c->funcs->pop_transform (c->data); + c->funcs->pop_clip (c->data); + c->funcs->pop_transform (c->data); + } + + HBUINT8 format; /* format = 10 */ + Offset24To paint; /* Offset (from beginning of PaintGlyph table) to Paint subtable. */ + HBUINT16 gid; + public: + DEFINE_SIZE_STATIC (6); +}; + +struct PaintColrGlyph +{ + void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer HB_UNUSED) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + return_trace (c->serializer->check_assign (out->gid, c->plan->glyph_map->get (gid), + HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + inline void paint_glyph (hb_paint_context_t *c) const; + + HBUINT8 format; /* format = 11 */ + HBUINT16 gid; + public: + DEFINE_SIZE_STATIC (3); +}; + +template class Var> +struct PaintTransform +{ + HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + if (!out->transform.serialize_subset (c, transform, this, instancer)) return_trace (false); + if (format == 13 && c->plan->all_axes_pinned) + out->format = 12; + return_trace (out->src.serialize_subset (c, src, this, instancer)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + src.sanitize (c, this) && + transform.sanitize (c, this)); + } + + void paint_glyph (hb_paint_context_t *c) const + { + TRACE_PAINT (this); + (this+transform).paint_glyph (c); + c->recurse (this+src); + c->funcs->pop_transform (c->data); + } + + HBUINT8 format; /* format = 12(noVar) or 13 (Var) */ + Offset24To src; /* Offset (from beginning of PaintTransform table) to Paint subtable. */ + Offset24To> transform; + public: + DEFINE_SIZE_STATIC (7); +}; + +struct PaintTranslate +{ + HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer, + uint32_t varIdxBase) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION) + { + out->dx = dx + (int) roundf (instancer (varIdxBase, 0)); + out->dy = dy + (int) roundf (instancer (varIdxBase, 1)); + } + + if (format == 15 && c->plan->all_axes_pinned) + out->format = 14; + + return_trace (out->src.serialize_subset (c, src, this, instancer)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && src.sanitize (c, this)); + } + + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + TRACE_PAINT (this); + float ddx = dx + c->instancer (varIdxBase, 0); + float ddy = dy + c->instancer (varIdxBase, 1); + + bool p1 = c->funcs->push_translate (c->data, ddx, ddy); + c->recurse (this+src); + if (p1) c->funcs->pop_transform (c->data); + } + + HBUINT8 format; /* format = 14(noVar) or 15 (Var) */ + Offset24To src; /* Offset (from beginning of PaintTranslate table) to Paint subtable. */ + FWORD dx; + FWORD dy; + public: + DEFINE_SIZE_STATIC (4 + 2 * FWORD::static_size); +}; + +struct PaintScale +{ + HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer, + uint32_t varIdxBase) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION) + { + out->scaleX.set_float (scaleX.to_float (instancer (varIdxBase, 0))); + out->scaleY.set_float (scaleY.to_float (instancer (varIdxBase, 1))); + } + + if (format == 17 && c->plan->all_axes_pinned) + out->format = 16; + + return_trace (out->src.serialize_subset (c, src, this, instancer)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && src.sanitize (c, this)); + } + + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + TRACE_PAINT (this); + float sx = scaleX.to_float (c->instancer (varIdxBase, 0)); + float sy = scaleY.to_float (c->instancer (varIdxBase, 1)); + + bool p1 = c->funcs->push_scale (c->data, sx, sy); + c->recurse (this+src); + if (p1) c->funcs->pop_transform (c->data); + } + + HBUINT8 format; /* format = 16 (noVar) or 17(Var) */ + Offset24To src; /* Offset (from beginning of PaintScale table) to Paint subtable. */ + F2DOT14 scaleX; + F2DOT14 scaleY; + public: + DEFINE_SIZE_STATIC (4 + 2 * F2DOT14::static_size); +}; + +struct PaintScaleAroundCenter +{ + HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer, + uint32_t varIdxBase) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION) + { + out->scaleX.set_float (scaleX.to_float (instancer (varIdxBase, 0))); + out->scaleY.set_float (scaleY.to_float (instancer (varIdxBase, 1))); + out->centerX = centerX + (int) roundf (instancer (varIdxBase, 2)); + out->centerY = centerY + (int) roundf (instancer (varIdxBase, 3)); + } + + if (format == 19 && c->plan->all_axes_pinned) + out->format = 18; + + return_trace (out->src.serialize_subset (c, src, this, instancer)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && src.sanitize (c, this)); + } + + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + TRACE_PAINT (this); + float sx = scaleX.to_float (c->instancer (varIdxBase, 0)); + float sy = scaleY.to_float (c->instancer (varIdxBase, 1)); + float tCenterX = centerX + c->instancer (varIdxBase, 2); + float tCenterY = centerY + c->instancer (varIdxBase, 3); + + bool p1 = c->funcs->push_translate (c->data, +tCenterX, +tCenterY); + bool p2 = c->funcs->push_scale (c->data, sx, sy); + bool p3 = c->funcs->push_translate (c->data, -tCenterX, -tCenterY); + c->recurse (this+src); + if (p3) c->funcs->pop_transform (c->data); + if (p2) c->funcs->pop_transform (c->data); + if (p1) c->funcs->pop_transform (c->data); + } + + HBUINT8 format; /* format = 18 (noVar) or 19(Var) */ + Offset24To src; /* Offset (from beginning of PaintScaleAroundCenter table) to Paint subtable. */ + F2DOT14 scaleX; + F2DOT14 scaleY; + FWORD centerX; + FWORD centerY; + public: + DEFINE_SIZE_STATIC (4 + 2 * F2DOT14::static_size + 2 * FWORD::static_size); +}; + +struct PaintScaleUniform +{ + HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer, + uint32_t varIdxBase) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION) + out->scale.set_float (scale.to_float (instancer (varIdxBase, 0))); + + if (format == 21 && c->plan->all_axes_pinned) + out->format = 20; + + return_trace (out->src.serialize_subset (c, src, this, instancer)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && src.sanitize (c, this)); + } + + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + TRACE_PAINT (this); + float s = scale.to_float (c->instancer (varIdxBase, 0)); + + bool p1 = c->funcs->push_scale (c->data, s, s); + c->recurse (this+src); + if (p1) c->funcs->pop_transform (c->data); + } + + HBUINT8 format; /* format = 20 (noVar) or 21(Var) */ + Offset24To src; /* Offset (from beginning of PaintScaleUniform table) to Paint subtable. */ + F2DOT14 scale; + public: + DEFINE_SIZE_STATIC (4 + F2DOT14::static_size); +}; + +struct PaintScaleUniformAroundCenter +{ + HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer, + uint32_t varIdxBase) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION) + { + out->scale.set_float (scale.to_float (instancer (varIdxBase, 0))); + out->centerX = centerX + (int) roundf (instancer (varIdxBase, 1)); + out->centerY = centerY + (int) roundf (instancer (varIdxBase, 2)); + } + + if (format == 23 && c->plan->all_axes_pinned) + out->format = 22; + + return_trace (out->src.serialize_subset (c, src, this, instancer)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && src.sanitize (c, this)); + } + + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + TRACE_PAINT (this); + float s = scale.to_float (c->instancer (varIdxBase, 0)); + float tCenterX = centerX + c->instancer (varIdxBase, 1); + float tCenterY = centerY + c->instancer (varIdxBase, 2); + + bool p1 = c->funcs->push_translate (c->data, +tCenterX, +tCenterY); + bool p2 = c->funcs->push_scale (c->data, s, s); + bool p3 = c->funcs->push_translate (c->data, -tCenterX, -tCenterY); + c->recurse (this+src); + if (p3) c->funcs->pop_transform (c->data); + if (p2) c->funcs->pop_transform (c->data); + if (p1) c->funcs->pop_transform (c->data); + } + + HBUINT8 format; /* format = 22 (noVar) or 23(Var) */ + Offset24To src; /* Offset (from beginning of PaintScaleUniformAroundCenter table) to Paint subtable. */ + F2DOT14 scale; + FWORD centerX; + FWORD centerY; + public: + DEFINE_SIZE_STATIC (4 + F2DOT14::static_size + 2 * FWORD::static_size); +}; + +struct PaintRotate +{ + HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer, + uint32_t varIdxBase) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION) + out->angle.set_float (angle.to_float (instancer (varIdxBase, 0))); + + if (format == 25 && c->plan->all_axes_pinned) + out->format = 24; + + return_trace (out->src.serialize_subset (c, src, this, instancer)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && src.sanitize (c, this)); + } + + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + TRACE_PAINT (this); + float a = angle.to_float (c->instancer (varIdxBase, 0)); + + bool p1 = c->funcs->push_rotate (c->data, a); + c->recurse (this+src); + if (p1) c->funcs->pop_transform (c->data); + } + + HBUINT8 format; /* format = 24 (noVar) or 25(Var) */ + Offset24To src; /* Offset (from beginning of PaintRotate table) to Paint subtable. */ + F2DOT14 angle; + public: + DEFINE_SIZE_STATIC (4 + F2DOT14::static_size); +}; + +struct PaintRotateAroundCenter +{ + HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer, + uint32_t varIdxBase) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION) + { + out->angle.set_float (angle.to_float (instancer (varIdxBase, 0))); + out->centerX = centerX + (int) roundf (instancer (varIdxBase, 1)); + out->centerY = centerY + (int) roundf (instancer (varIdxBase, 2)); + } + + if (format ==27 && c->plan->all_axes_pinned) + out->format = 26; + + return_trace (out->src.serialize_subset (c, src, this, instancer)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && src.sanitize (c, this)); + } + + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + TRACE_PAINT (this); + float a = angle.to_float (c->instancer (varIdxBase, 0)); + float tCenterX = centerX + c->instancer (varIdxBase, 1); + float tCenterY = centerY + c->instancer (varIdxBase, 2); + + bool p1 = c->funcs->push_translate (c->data, +tCenterX, +tCenterY); + bool p2 = c->funcs->push_rotate (c->data, a); + bool p3 = c->funcs->push_translate (c->data, -tCenterX, -tCenterY); + c->recurse (this+src); + if (p3) c->funcs->pop_transform (c->data); + if (p2) c->funcs->pop_transform (c->data); + if (p1) c->funcs->pop_transform (c->data); + } + + HBUINT8 format; /* format = 26 (noVar) or 27(Var) */ + Offset24To src; /* Offset (from beginning of PaintRotateAroundCenter table) to Paint subtable. */ + F2DOT14 angle; + FWORD centerX; + FWORD centerY; + public: + DEFINE_SIZE_STATIC (4 + F2DOT14::static_size + 2 * FWORD::static_size); +}; + +struct PaintSkew +{ + HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer, + uint32_t varIdxBase) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION) + { + out->xSkewAngle.set_float (xSkewAngle.to_float (instancer (varIdxBase, 0))); + out->ySkewAngle.set_float (ySkewAngle.to_float (instancer (varIdxBase, 1))); + } + + if (format == 29 && c->plan->all_axes_pinned) + out->format = 28; + + return_trace (out->src.serialize_subset (c, src, this, instancer)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && src.sanitize (c, this)); + } + + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + TRACE_PAINT (this); + float sx = xSkewAngle.to_float(c->instancer (varIdxBase, 0)); + float sy = ySkewAngle.to_float(c->instancer (varIdxBase, 1)); + + bool p1 = c->funcs->push_skew (c->data, sx, sy); + c->recurse (this+src); + if (p1) c->funcs->pop_transform (c->data); + } + + HBUINT8 format; /* format = 28(noVar) or 29 (Var) */ + Offset24To src; /* Offset (from beginning of PaintSkew table) to Paint subtable. */ + F2DOT14 xSkewAngle; + F2DOT14 ySkewAngle; + public: + DEFINE_SIZE_STATIC (4 + 2 * F2DOT14::static_size); +}; + +struct PaintSkewAroundCenter +{ + HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer, + uint32_t varIdxBase) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION) + { + out->xSkewAngle.set_float (xSkewAngle.to_float (instancer (varIdxBase, 0))); + out->ySkewAngle.set_float (ySkewAngle.to_float (instancer (varIdxBase, 1))); + out->centerX = centerX + (int) roundf (instancer (varIdxBase, 2)); + out->centerY = centerY + (int) roundf (instancer (varIdxBase, 3)); + } + + if (format == 31 && c->plan->all_axes_pinned) + out->format = 30; + + return_trace (out->src.serialize_subset (c, src, this, instancer)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && src.sanitize (c, this)); + } + + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + TRACE_PAINT (this); + float sx = xSkewAngle.to_float(c->instancer (varIdxBase, 0)); + float sy = ySkewAngle.to_float(c->instancer (varIdxBase, 1)); + float tCenterX = centerX + c->instancer (varIdxBase, 2); + float tCenterY = centerY + c->instancer (varIdxBase, 3); + + bool p1 = c->funcs->push_translate (c->data, +tCenterX, +tCenterY); + bool p2 = c->funcs->push_skew (c->data, sx, sy); + bool p3 = c->funcs->push_translate (c->data, -tCenterX, -tCenterY); + c->recurse (this+src); + if (p3) c->funcs->pop_transform (c->data); + if (p2) c->funcs->pop_transform (c->data); + if (p1) c->funcs->pop_transform (c->data); + } + + HBUINT8 format; /* format = 30(noVar) or 31 (Var) */ + Offset24To src; /* Offset (from beginning of PaintSkewAroundCenter table) to Paint subtable. */ + F2DOT14 xSkewAngle; + F2DOT14 ySkewAngle; + FWORD centerX; + FWORD centerY; + public: + DEFINE_SIZE_STATIC (4 + 2 * F2DOT14::static_size + 2 * FWORD::static_size); +}; + +struct PaintComposite +{ + void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + bool ret = false; + ret |= out->src.serialize_subset (c, src, this, instancer); + ret |= out->backdrop.serialize_subset (c, backdrop, this, instancer); + return_trace (ret); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + c->check_ops (this->min_size) && // PainComposite can get exponential + src.sanitize (c, this) && + backdrop.sanitize (c, this)); + } + + void paint_glyph (hb_paint_context_t *c) const + { + TRACE_PAINT (this); + c->recurse (this+backdrop); + c->funcs->push_group (c->data); + c->recurse (this+src); + c->funcs->pop_group (c->data, (hb_paint_composite_mode_t) (int) mode); + } + + HBUINT8 format; /* format = 32 */ + Offset24To src; /* Offset (from beginning of PaintComposite table) to source Paint subtable. */ + CompositeMode mode; /* If mode is unrecognized use COMPOSITE_CLEAR */ + Offset24To backdrop; /* Offset (from beginning of PaintComposite table) to backdrop Paint subtable. */ + public: + DEFINE_SIZE_STATIC (8); +}; + +struct ClipBoxData +{ + int xMin, yMin, xMax, yMax; +}; + +struct ClipBoxFormat1 +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + void get_clip_box (ClipBoxData &clip_box, const VarStoreInstancer &instancer HB_UNUSED) const + { + clip_box.xMin = xMin; + clip_box.yMin = yMin; + clip_box.xMax = xMax; + clip_box.yMax = yMax; + } + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer, + uint32_t varIdxBase) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (*this); + if (unlikely (!out)) return_trace (false); + + if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION) + { + out->xMin = xMin + (int) roundf (instancer (varIdxBase, 0)); + out->yMin = yMin + (int) roundf (instancer (varIdxBase, 1)); + out->xMax = xMax + (int) roundf (instancer (varIdxBase, 2)); + out->yMax = yMax + (int) roundf (instancer (varIdxBase, 3)); + } + + if (format == 2 && c->plan->all_axes_pinned) + out->format = 1; + + return_trace (true); + } + + public: + HBUINT8 format; /* format = 1(noVar) or 2(Var)*/ + FWORD xMin; + FWORD yMin; + FWORD xMax; + FWORD yMax; + public: + DEFINE_SIZE_STATIC (1 + 4 * FWORD::static_size); +}; + +struct ClipBoxFormat2 : Variable +{ + void get_clip_box (ClipBoxData &clip_box, const VarStoreInstancer &instancer) const + { + value.get_clip_box(clip_box, instancer); + if (instancer) + { + clip_box.xMin += roundf (instancer (varIdxBase, 0)); + clip_box.yMin += roundf (instancer (varIdxBase, 1)); + clip_box.xMax += roundf (instancer (varIdxBase, 2)); + clip_box.yMax += roundf (instancer (varIdxBase, 3)); + } + } +}; + +struct ClipBox +{ + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer) const + { + TRACE_SUBSET (this); + switch (u.format) { + case 1: return_trace (u.format1.subset (c, instancer, VarIdx::NO_VARIATION)); + case 2: return_trace (u.format2.subset (c, instancer)); + default:return_trace (c->default_return_value ()); + } + } + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); + default:return_trace (c->default_return_value ()); + } + } + + bool get_extents (hb_glyph_extents_t *extents, + const VarStoreInstancer &instancer) const + { + ClipBoxData clip_box; + switch (u.format) { + case 1: + u.format1.get_clip_box (clip_box, instancer); + break; + case 2: + u.format2.get_clip_box (clip_box, instancer); + break; + default: + return false; + } + + extents->x_bearing = clip_box.xMin; + extents->y_bearing = clip_box.yMax; + extents->width = clip_box.xMax - clip_box.xMin; + extents->height = clip_box.yMin - clip_box.yMax; + return true; + } + + protected: + union { + HBUINT8 format; /* Format identifier */ + ClipBoxFormat1 format1; + ClipBoxFormat2 format2; + } u; +}; + +struct ClipRecord +{ + int cmp (hb_codepoint_t g) const + { return g < startGlyphID ? -1 : g <= endGlyphID ? 0 : +1; } + + bool subset (hb_subset_context_t *c, + const void *base, + const VarStoreInstancer &instancer) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (*this); + if (unlikely (!out)) return_trace (false); + + return_trace (out->clipBox.serialize_subset (c, clipBox, base, instancer)); + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && clipBox.sanitize (c, base)); + } + + bool get_extents (hb_glyph_extents_t *extents, + const void *base, + const VarStoreInstancer &instancer) const + { + return (base+clipBox).get_extents (extents, instancer); + } + + public: + HBUINT16 startGlyphID; // first gid clip applies to + HBUINT16 endGlyphID; // last gid clip applies to, inclusive + Offset24To clipBox; // Box or VarBox + public: + DEFINE_SIZE_STATIC (7); +}; +DECLARE_NULL_NAMESPACE_BYTES (OT, ClipRecord); + +struct ClipList +{ + unsigned serialize_clip_records (hb_subset_context_t *c, + const VarStoreInstancer &instancer, + const hb_set_t& gids, + const hb_map_t& gid_offset_map) const + { + TRACE_SERIALIZE (this); + if (gids.is_empty () || + gid_offset_map.get_population () != gids.get_population ()) + return_trace (0); + + unsigned count = 0; + + hb_codepoint_t start_gid= gids.get_min (); + hb_codepoint_t prev_gid = start_gid; + + unsigned offset = gid_offset_map.get (start_gid); + unsigned prev_offset = offset; + for (const hb_codepoint_t _ : gids.iter ()) + { + if (_ == start_gid) continue; + + offset = gid_offset_map.get (_); + if (_ == prev_gid + 1 && offset == prev_offset) + { + prev_gid = _; + continue; + } + + ClipRecord record; + record.startGlyphID = start_gid; + record.endGlyphID = prev_gid; + record.clipBox = prev_offset; + + if (!record.subset (c, this, instancer)) return_trace (0); + count++; + + start_gid = _; + prev_gid = _; + prev_offset = offset; + } + + //last one + { + ClipRecord record; + record.startGlyphID = start_gid; + record.endGlyphID = prev_gid; + record.clipBox = prev_offset; + if (!record.subset (c, this, instancer)) return_trace (0); + count++; + } + return_trace (count); + } + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + if (!c->serializer->check_assign (out->format, format, HB_SERIALIZE_ERROR_INT_OVERFLOW)) return_trace (false); + + const hb_set_t& glyphset = c->plan->_glyphset_colred; + const hb_map_t &glyph_map = *c->plan->glyph_map; + + hb_map_t new_gid_offset_map; + hb_set_t new_gids; + for (const ClipRecord& record : clips.iter ()) + { + unsigned start_gid = record.startGlyphID; + unsigned end_gid = record.endGlyphID; + for (unsigned gid = start_gid; gid <= end_gid; gid++) + { + if (!glyphset.has (gid) || !glyph_map.has (gid)) continue; + unsigned new_gid = glyph_map.get (gid); + new_gid_offset_map.set (new_gid, record.clipBox); + new_gids.add (new_gid); + } + } + + unsigned count = serialize_clip_records (c, instancer, new_gids, new_gid_offset_map); + if (!count) return_trace (false); + return_trace (c->serializer->check_assign (out->clips.len, count, HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + // TODO Make a formatted struct! + return_trace (c->check_struct (this) && clips.sanitize (c, this)); + } + + bool + get_extents (hb_codepoint_t gid, + hb_glyph_extents_t *extents, + const VarStoreInstancer &instancer) const + { + auto *rec = clips.as_array ().bsearch (gid); + if (rec) + { + rec->get_extents (extents, this, instancer); + return true; + } + return false; + } + + HBUINT8 format; // Set to 1. + SortedArray32Of clips; // Clip records, sorted by startGlyphID + public: + DEFINE_SIZE_ARRAY_SIZED (5, clips); +}; + +struct Paint +{ + + template + bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const + { + TRACE_SANITIZE (this); + + if (unlikely (!c->check_start_recursion (HB_MAX_NESTING_LEVEL))) + return_trace (c->no_dispatch_return_value ()); + + return_trace (c->end_recursion (this->dispatch (c, std::forward (ds)...))); + } + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format); + switch (u.format) { + case 1: return_trace (c->dispatch (u.paintformat1, std::forward (ds)...)); + case 2: return_trace (c->dispatch (u.paintformat2, std::forward (ds)...)); + case 3: return_trace (c->dispatch (u.paintformat3, std::forward (ds)...)); + case 4: return_trace (c->dispatch (u.paintformat4, std::forward (ds)...)); + case 5: return_trace (c->dispatch (u.paintformat5, std::forward (ds)...)); + case 6: return_trace (c->dispatch (u.paintformat6, std::forward (ds)...)); + case 7: return_trace (c->dispatch (u.paintformat7, std::forward (ds)...)); + case 8: return_trace (c->dispatch (u.paintformat8, std::forward (ds)...)); + case 9: return_trace (c->dispatch (u.paintformat9, std::forward (ds)...)); + case 10: return_trace (c->dispatch (u.paintformat10, std::forward (ds)...)); + case 11: return_trace (c->dispatch (u.paintformat11, std::forward (ds)...)); + case 12: return_trace (c->dispatch (u.paintformat12, std::forward (ds)...)); + case 13: return_trace (c->dispatch (u.paintformat13, std::forward (ds)...)); + case 14: return_trace (c->dispatch (u.paintformat14, std::forward (ds)...)); + case 15: return_trace (c->dispatch (u.paintformat15, std::forward (ds)...)); + case 16: return_trace (c->dispatch (u.paintformat16, std::forward (ds)...)); + case 17: return_trace (c->dispatch (u.paintformat17, std::forward (ds)...)); + case 18: return_trace (c->dispatch (u.paintformat18, std::forward (ds)...)); + case 19: return_trace (c->dispatch (u.paintformat19, std::forward (ds)...)); + case 20: return_trace (c->dispatch (u.paintformat20, std::forward (ds)...)); + case 21: return_trace (c->dispatch (u.paintformat21, std::forward (ds)...)); + case 22: return_trace (c->dispatch (u.paintformat22, std::forward (ds)...)); + case 23: return_trace (c->dispatch (u.paintformat23, std::forward (ds)...)); + case 24: return_trace (c->dispatch (u.paintformat24, std::forward (ds)...)); + case 25: return_trace (c->dispatch (u.paintformat25, std::forward (ds)...)); + case 26: return_trace (c->dispatch (u.paintformat26, std::forward (ds)...)); + case 27: return_trace (c->dispatch (u.paintformat27, std::forward (ds)...)); + case 28: return_trace (c->dispatch (u.paintformat28, std::forward (ds)...)); + case 29: return_trace (c->dispatch (u.paintformat29, std::forward (ds)...)); + case 30: return_trace (c->dispatch (u.paintformat30, std::forward (ds)...)); + case 31: return_trace (c->dispatch (u.paintformat31, std::forward (ds)...)); + case 32: return_trace (c->dispatch (u.paintformat32, std::forward (ds)...)); + default:return_trace (c->default_return_value ()); + } + } + + protected: + union { + HBUINT8 format; + PaintColrLayers paintformat1; + NoVariable paintformat2; + Variable paintformat3; + NoVariable> paintformat4; + Variable> paintformat5; + NoVariable> paintformat6; + Variable> paintformat7; + NoVariable> paintformat8; + Variable> paintformat9; + PaintGlyph paintformat10; + PaintColrGlyph paintformat11; + PaintTransform paintformat12; + PaintTransform paintformat13; + NoVariable paintformat14; + Variable paintformat15; + NoVariable paintformat16; + Variable paintformat17; + NoVariable paintformat18; + Variable paintformat19; + NoVariable paintformat20; + Variable paintformat21; + NoVariable paintformat22; + Variable paintformat23; + NoVariable paintformat24; + Variable paintformat25; + NoVariable paintformat26; + Variable paintformat27; + NoVariable paintformat28; + Variable paintformat29; + NoVariable paintformat30; + Variable paintformat31; + PaintComposite paintformat32; + } u; + public: + DEFINE_SIZE_MIN (2); +}; + +struct BaseGlyphPaintRecord +{ + int cmp (hb_codepoint_t g) const + { return g < glyphId ? -1 : g > glyphId ? 1 : 0; } + + bool serialize (hb_serialize_context_t *s, const hb_map_t* glyph_map, + const void* src_base, hb_subset_context_t *c, + const VarStoreInstancer &instancer) const + { + TRACE_SERIALIZE (this); + auto *out = s->embed (this); + if (unlikely (!out)) return_trace (false); + if (!s->check_assign (out->glyphId, glyph_map->get (glyphId), + HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); + + return_trace (out->paint.serialize_subset (c, paint, src_base, instancer)); + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && paint.sanitize (c, base))); + } + + public: + HBGlyphID16 glyphId; /* Glyph ID of reference glyph */ + Offset32To paint; /* Offset (from beginning of BaseGlyphPaintRecord array) to Paint, + * Typically PaintColrLayers */ + public: + DEFINE_SIZE_STATIC (6); +}; + +struct BaseGlyphList : SortedArray32Of +{ + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + const hb_set_t* glyphset = &c->plan->_glyphset_colred; + + for (const auto& _ : as_array ()) + { + unsigned gid = _.glyphId; + if (!glyphset->has (gid)) continue; + + if (_.serialize (c->serializer, c->plan->glyph_map, this, c, instancer)) out->len++; + else return_trace (false); + } + + return_trace (out->len != 0); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (SortedArray32Of::sanitize (c, this)); + } +}; + +struct LayerList : Array32OfOffset32To +{ + const Paint& get_paint (unsigned i) const + { return this+(*this)[i]; } + + bool subset (hb_subset_context_t *c, + const VarStoreInstancer &instancer) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + bool ret = false; + for (const auto& _ : + hb_enumerate (*this) + | hb_filter (c->plan->colrv1_layers, hb_first)) + + { + auto *o = out->serialize_append (c->serializer); + if (unlikely (!o)) return_trace (false); + ret |= o->serialize_subset (c, _.second, this, instancer); + } + return_trace (ret); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (Array32OfOffset32To::sanitize (c, this)); + } +}; + +struct COLR +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_COLR; + + bool has_v0_data () const { return numBaseGlyphs; } + bool has_v1_data () const + { + if (version == 1) + return (this+baseGlyphList).len > 0; + + return false; + } + + unsigned int get_glyph_layers (hb_codepoint_t glyph, + unsigned int start_offset, + unsigned int *count, /* IN/OUT. May be NULL. */ + hb_ot_color_layer_t *layers /* OUT. May be NULL. */) const + { + const BaseGlyphRecord &record = (this+baseGlyphsZ).bsearch (numBaseGlyphs, glyph); + + hb_array_t all_layers = (this+layersZ).as_array (numLayers); + hb_array_t glyph_layers = all_layers.sub_array (record.firstLayerIdx, + record.numLayers); + if (count) + { + + glyph_layers.sub_array (start_offset, count) + | hb_sink (hb_array (layers, *count)) + ; + } + return glyph_layers.length; + } + + struct accelerator_t + { + accelerator_t (hb_face_t *face) + { colr = hb_sanitize_context_t ().reference_table (face); } + ~accelerator_t () { this->colr.destroy (); } + + bool is_valid () { return colr.get_blob ()->length; } + + void closure_glyphs (hb_codepoint_t glyph, + hb_set_t *related_ids /* OUT */) const + { colr->closure_glyphs (glyph, related_ids); } + + void closure_V0palette_indices (const hb_set_t *glyphs, + hb_set_t *palettes /* OUT */) const + { colr->closure_V0palette_indices (glyphs, palettes); } + + void closure_forV1 (hb_set_t *glyphset, + hb_set_t *layer_indices, + hb_set_t *palette_indices) const + { colr->closure_forV1 (glyphset, layer_indices, palette_indices); } + + private: + hb_blob_ptr_t colr; + }; + + void closure_glyphs (hb_codepoint_t glyph, + hb_set_t *related_ids /* OUT */) const + { + const BaseGlyphRecord *record = get_base_glyph_record (glyph); + if (!record) return; + + auto glyph_layers = (this+layersZ).as_array (numLayers).sub_array (record->firstLayerIdx, + record->numLayers); + if (!glyph_layers.length) return; + related_ids->add_array (&glyph_layers[0].glyphId, glyph_layers.length, LayerRecord::min_size); + } + + void closure_V0palette_indices (const hb_set_t *glyphs, + hb_set_t *palettes /* OUT */) const + { + if (!numBaseGlyphs || !numLayers) return; + hb_array_t baseGlyphs = (this+baseGlyphsZ).as_array (numBaseGlyphs); + hb_array_t all_layers = (this+layersZ).as_array (numLayers); + + for (const BaseGlyphRecord record : baseGlyphs) + { + if (!glyphs->has (record.glyphId)) continue; + hb_array_t glyph_layers = all_layers.sub_array (record.firstLayerIdx, + record.numLayers); + for (const LayerRecord layer : glyph_layers) + palettes->add (layer.colorIdx); + } + } + + void closure_forV1 (hb_set_t *glyphset, + hb_set_t *layer_indices, + hb_set_t *palette_indices) const + { + if (version != 1) return; + hb_set_t visited_glyphs; + + hb_colrv1_closure_context_t c (this, &visited_glyphs, layer_indices, palette_indices); + const BaseGlyphList &baseglyph_paintrecords = this+baseGlyphList; + + for (const BaseGlyphPaintRecord &baseglyph_paintrecord: baseglyph_paintrecords.iter ()) + { + unsigned gid = baseglyph_paintrecord.glyphId; + if (!glyphset->has (gid)) continue; + + const Paint &paint = &baseglyph_paintrecords+baseglyph_paintrecord.paint; + paint.dispatch (&c); + } + hb_set_union (glyphset, &visited_glyphs); + } + + const LayerList& get_layerList () const + { return (this+layerList); } + + const BaseGlyphList& get_baseglyphList () const + { return (this+baseGlyphList); } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + (this+baseGlyphsZ).sanitize (c, numBaseGlyphs) && + (this+layersZ).sanitize (c, numLayers) && + (version == 0 || + (version == 1 && + baseGlyphList.sanitize (c, this) && + layerList.sanitize (c, this) && + clipList.sanitize (c, this) && + varIdxMap.sanitize (c, this) && + varStore.sanitize (c, this)))); + } + + template + bool serialize_V0 (hb_serialize_context_t *c, + unsigned version, + BaseIterator base_it, + LayerIterator layer_it) + { + TRACE_SERIALIZE (this); + if (unlikely (base_it.len () != layer_it.len ())) + return_trace (false); + + this->version = version; + numLayers = 0; + numBaseGlyphs = base_it.len (); + if (numBaseGlyphs == 0) + { + baseGlyphsZ = 0; + layersZ = 0; + return_trace (true); + } + + c->push (); + for (const hb_item_type _ : + base_it.iter ()) + { + auto* record = c->embed (_); + if (unlikely (!record)) return_trace (false); + record->firstLayerIdx = numLayers; + numLayers += record->numLayers; + } + c->add_link (baseGlyphsZ, c->pop_pack ()); + + c->push (); + for (const hb_item_type& _ : + layer_it.iter ()) + _.as_array ().copy (c); + + c->add_link (layersZ, c->pop_pack ()); + + return_trace (true); + } + + const BaseGlyphRecord* get_base_glyph_record (hb_codepoint_t gid) const + { + const BaseGlyphRecord* record = &(this+baseGlyphsZ).bsearch (numBaseGlyphs, (unsigned int) gid); + if (record == &Null (BaseGlyphRecord) || + (record && (hb_codepoint_t) record->glyphId != gid)) + record = nullptr; + return record; + } + + const BaseGlyphPaintRecord* get_base_glyph_paintrecord (hb_codepoint_t gid) const + { + const BaseGlyphPaintRecord* record = &(this+baseGlyphList).bsearch ((unsigned) gid); + if ((record && (hb_codepoint_t) record->glyphId != gid)) + record = nullptr; + return record; + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_map_t &reverse_glyph_map = *c->plan->reverse_glyph_map; + const hb_set_t& glyphset = c->plan->_glyphset_colred; + + auto base_it = + + hb_range (c->plan->num_output_glyphs ()) + | hb_filter ([&](hb_codepoint_t new_gid) + { + hb_codepoint_t old_gid = reverse_glyph_map.get (new_gid); + if (glyphset.has (old_gid)) return true; + return false; + }) + | hb_map_retains_sorting ([&](hb_codepoint_t new_gid) + { + hb_codepoint_t old_gid = reverse_glyph_map.get (new_gid); + + const BaseGlyphRecord* old_record = get_base_glyph_record (old_gid); + if (unlikely (!old_record)) + return hb_pair_t (false, Null (BaseGlyphRecord)); + BaseGlyphRecord new_record = {}; + new_record.glyphId = new_gid; + new_record.numLayers = old_record->numLayers; + return hb_pair_t (true, new_record); + }) + | hb_filter (hb_first) + | hb_map_retains_sorting (hb_second) + ; + + auto layer_it = + + hb_range (c->plan->num_output_glyphs ()) + | hb_map (reverse_glyph_map) + | hb_filter (glyphset) + | hb_map_retains_sorting ([&](hb_codepoint_t old_gid) + { + const BaseGlyphRecord* old_record = get_base_glyph_record (old_gid); + hb_vector_t out_layers; + + if (unlikely (!old_record || + old_record->firstLayerIdx >= numLayers || + old_record->firstLayerIdx + old_record->numLayers > numLayers)) + return hb_pair_t> (false, out_layers); + + auto layers = (this+layersZ).as_array (numLayers).sub_array (old_record->firstLayerIdx, + old_record->numLayers); + out_layers.resize (layers.length); + for (unsigned int i = 0; i < layers.length; i++) { + out_layers[i] = layers[i]; + hb_codepoint_t new_gid = 0; + if (unlikely (!c->plan->new_gid_for_old_gid (out_layers[i].glyphId, &new_gid))) + return hb_pair_t> (false, out_layers); + out_layers[i].glyphId = new_gid; + out_layers[i].colorIdx = c->plan->colr_palettes.get (layers[i].colorIdx); + } + + return hb_pair_t> (true, out_layers); + }) + | hb_filter (hb_first) + | hb_map_retains_sorting (hb_second) + ; + + if (version == 0 && (!base_it || !layer_it)) + return_trace (false); + + auto *colr_prime = c->serializer->start_embed (); + if (unlikely (!c->serializer->extend_min (colr_prime))) return_trace (false); + + if (version == 0) + return_trace (colr_prime->serialize_V0 (c->serializer, version, base_it, layer_it)); + + auto snap = c->serializer->snapshot (); + if (!c->serializer->allocate_size (5 * HBUINT32::static_size)) return_trace (false); + + VarStoreInstancer instancer (varStore ? &(this+varStore) : nullptr, + varIdxMap ? &(this+varIdxMap) : nullptr, + c->plan->normalized_coords.as_array ()); + + if (!colr_prime->baseGlyphList.serialize_subset (c, baseGlyphList, this, instancer)) + { + if (c->serializer->in_error ()) return_trace (false); + //no more COLRv1 glyphs: downgrade to version 0 + c->serializer->revert (snap); + return_trace (colr_prime->serialize_V0 (c->serializer, 0, base_it, layer_it)); + } + + if (!colr_prime->serialize_V0 (c->serializer, version, base_it, layer_it)) return_trace (false); + + colr_prime->layerList.serialize_subset (c, layerList, this, instancer); + colr_prime->clipList.serialize_subset (c, clipList, this, instancer); + if (!varStore || c->plan->all_axes_pinned) + return_trace (true); + + colr_prime->varIdxMap.serialize_copy (c->serializer, varIdxMap, this); + colr_prime->varStore.serialize_copy (c->serializer, varStore, this); + return_trace (true); + } + + const Paint *get_base_glyph_paint (hb_codepoint_t glyph) const + { + const BaseGlyphList &baseglyph_paintrecords = this+baseGlyphList; + const BaseGlyphPaintRecord* record = get_base_glyph_paintrecord (glyph); + if (record) + { + const Paint &paint = &baseglyph_paintrecords+record->paint; + return &paint; + } + else + return nullptr; + } + +#ifndef HB_NO_PAINT + bool + get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const + { + if (version != 1) + return false; + + VarStoreInstancer instancer (&(this+varStore), + &(this+varIdxMap), + hb_array (font->coords, font->num_coords)); + + if (get_clip (glyph, extents, instancer)) + { + font->scale_glyph_extents (extents); + return true; + } + + auto *extents_funcs = hb_paint_extents_get_funcs (); + hb_paint_extents_context_t extents_data; + bool ret = paint_glyph (font, glyph, extents_funcs, &extents_data, 0, HB_COLOR(0,0,0,0)); + + hb_extents_t e = extents_data.get_extents (); + if (e.is_void ()) + { + extents->x_bearing = 0; + extents->y_bearing = 0; + extents->width = 0; + extents->height = 0; + } + else + { + extents->x_bearing = e.xmin; + extents->y_bearing = e.ymax; + extents->width = e.xmax - e.xmin; + extents->height = e.ymin - e.ymax; + } + + return ret; + } +#endif + + bool + has_paint_for_glyph (hb_codepoint_t glyph) const + { + if (version == 1) + { + const Paint *paint = get_base_glyph_paint (glyph); + + return paint != nullptr; + } + + return false; + } + + bool get_clip (hb_codepoint_t glyph, + hb_glyph_extents_t *extents, + const VarStoreInstancer instancer) const + { + return (this+clipList).get_extents (glyph, + extents, + instancer); + } + +#ifndef HB_NO_PAINT + bool + paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, unsigned int palette_index, hb_color_t foreground, bool clip = true) const + { + VarStoreInstancer instancer (&(this+varStore), + &(this+varIdxMap), + hb_array (font->coords, font->num_coords)); + hb_paint_context_t c (this, funcs, data, font, palette_index, foreground, instancer); + c.current_glyphs.add (glyph); + + if (version == 1) + { + const Paint *paint = get_base_glyph_paint (glyph); + if (paint) + { + // COLRv1 glyph + + VarStoreInstancer instancer (&(this+varStore), + &(this+varIdxMap), + hb_array (font->coords, font->num_coords)); + + bool is_bounded = true; + if (clip) + { + hb_glyph_extents_t extents; + if (get_clip (glyph, &extents, instancer)) + { + font->scale_glyph_extents (&extents); + c.funcs->push_clip_rectangle (c.data, + extents.x_bearing, + extents.y_bearing + extents.height, + extents.x_bearing + extents.width, + extents.y_bearing); + } + else + { + auto *extents_funcs = hb_paint_extents_get_funcs (); + hb_paint_extents_context_t extents_data; + + paint_glyph (font, glyph, + extents_funcs, &extents_data, + palette_index, foreground, + false); + + hb_extents_t extents = extents_data.get_extents (); + is_bounded = extents_data.is_bounded (); + + c.funcs->push_clip_rectangle (c.data, + extents.xmin, + extents.ymin, + extents.xmax, + extents.ymax); + } + } + + c.funcs->push_root_transform (c.data, font); + + if (is_bounded) + c.recurse (*paint); + + c.funcs->pop_transform (c.data); + + if (clip) + c.funcs->pop_clip (c.data); + + return true; + } + } + + const BaseGlyphRecord *record = get_base_glyph_record (glyph); + if (record && ((hb_codepoint_t) record->glyphId == glyph)) + { + // COLRv0 glyph + for (const auto &r : (this+layersZ).as_array (numLayers) + .sub_array (record->firstLayerIdx, record->numLayers)) + { + hb_bool_t is_foreground; + hb_color_t color = c.get_color (r.colorIdx, 1., &is_foreground); + c.funcs->push_clip_glyph (c.data, r.glyphId, c.font); + c.funcs->color (c.data, is_foreground, color); + c.funcs->pop_clip (c.data); + } + + return true; + } + + return false; + } +#endif + + protected: + HBUINT16 version; /* Table version number (starts at 0). */ + HBUINT16 numBaseGlyphs; /* Number of Base Glyph Records. */ + NNOffset32To> + baseGlyphsZ; /* Offset to Base Glyph records. */ + NNOffset32To> + layersZ; /* Offset to Layer Records. */ + HBUINT16 numLayers; /* Number of Layer Records. */ + // Version-1 additions + Offset32To baseGlyphList; + Offset32To layerList; + Offset32To clipList; // Offset to ClipList table (may be NULL) + Offset32To varIdxMap; // Offset to DeltaSetIndexMap table (may be NULL) + Offset32To varStore; + public: + DEFINE_SIZE_MIN (14); +}; + +struct COLR_accelerator_t : COLR::accelerator_t { + COLR_accelerator_t (hb_face_t *face) : COLR::accelerator_t (face) {} +}; + +void +hb_paint_context_t::recurse (const Paint &paint) +{ + if (unlikely (depth_left <= 0 || edge_count <= 0)) return; + depth_left--; + edge_count--; + paint.dispatch (this); + depth_left++; +} + +void PaintColrLayers::paint_glyph (hb_paint_context_t *c) const +{ + TRACE_PAINT (this); + const LayerList &paint_offset_lists = c->get_colr_table ()->get_layerList (); + for (unsigned i = firstLayerIndex; i < firstLayerIndex + numLayers; i++) + { + if (unlikely (c->current_layers.has (i))) + continue; + + c->current_layers.add (i); + + const Paint &paint = paint_offset_lists.get_paint (i); + c->funcs->push_group (c->data); + c->recurse (paint); + c->funcs->pop_group (c->data, HB_PAINT_COMPOSITE_MODE_SRC_OVER); + + c->current_layers.del (i); + } +} + +void PaintColrGlyph::paint_glyph (hb_paint_context_t *c) const +{ + TRACE_PAINT (this); + + if (unlikely (c->current_glyphs.has (gid))) + return; + + c->current_glyphs.add (gid); + + c->funcs->push_inverse_root_transform (c->data, c->font); + if (c->funcs->color_glyph (c->data, gid, c->font)) + { + c->funcs->pop_transform (c->data); + c->current_glyphs.del (gid); + return; + } + c->funcs->pop_transform (c->data); + + const COLR *colr_table = c->get_colr_table (); + const Paint *paint = colr_table->get_base_glyph_paint (gid); + + hb_glyph_extents_t extents = {0}; + bool has_clip_box = colr_table->get_clip (gid, &extents, c->instancer); + + if (has_clip_box) + c->funcs->push_clip_rectangle (c->data, + extents.x_bearing, + extents.y_bearing + extents.height, + extents.x_bearing + extents.width, + extents.y_bearing); + + if (paint) + c->recurse (*paint); + + if (has_clip_box) + c->funcs->pop_clip (c->data); + + c->current_glyphs.del (gid); +} + +} /* namespace OT */ + +#endif /* OT_COLOR_COLR_COLR_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Color/COLR/colrv1-closure.hh b/src/3rdparty/harfbuzz-ng/src/OT/Color/COLR/colrv1-closure.hh new file mode 100644 index 00000000000..705863d4ade --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Color/COLR/colrv1-closure.hh @@ -0,0 +1,107 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * Copyright © 2020 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. + * + */ + +#ifndef OT_COLOR_COLR_COLRV1_CLOSURE_HH +#define OT_COLOR_COLR_COLRV1_CLOSURE_HH + +#include "../../../hb-open-type.hh" +#include "COLR.hh" + +/* + * COLR -- Color + * https://docs.microsoft.com/en-us/typography/opentype/spec/colr + */ +namespace OT { + +HB_INTERNAL void PaintColrLayers::closurev1 (hb_colrv1_closure_context_t* c) const +{ + c->add_layer_indices (firstLayerIndex, numLayers); + const LayerList &paint_offset_lists = c->get_colr_table ()->get_layerList (); + for (unsigned i = firstLayerIndex; i < firstLayerIndex + numLayers; i++) + { + const Paint &paint = std::addressof (paint_offset_lists) + paint_offset_lists[i]; + paint.dispatch (c); + } +} + +HB_INTERNAL void PaintGlyph::closurev1 (hb_colrv1_closure_context_t* c) const +{ + c->add_glyph (gid); + (this+paint).dispatch (c); +} + +HB_INTERNAL void PaintColrGlyph::closurev1 (hb_colrv1_closure_context_t* c) const +{ + const COLR *colr_table = c->get_colr_table (); + const BaseGlyphPaintRecord* baseglyph_paintrecord = colr_table->get_base_glyph_paintrecord (gid); + if (!baseglyph_paintrecord) return; + c->add_glyph (gid); + + const BaseGlyphList &baseglyph_list = colr_table->get_baseglyphList (); + (&baseglyph_list+baseglyph_paintrecord->paint).dispatch (c); +} + +template class Var> +HB_INTERNAL void PaintTransform::closurev1 (hb_colrv1_closure_context_t* c) const +{ (this+src).dispatch (c); } + +HB_INTERNAL void PaintTranslate::closurev1 (hb_colrv1_closure_context_t* c) const +{ (this+src).dispatch (c); } + +HB_INTERNAL void PaintScale::closurev1 (hb_colrv1_closure_context_t* c) const +{ (this+src).dispatch (c); } + +HB_INTERNAL void PaintScaleAroundCenter::closurev1 (hb_colrv1_closure_context_t* c) const +{ (this+src).dispatch (c); } + +HB_INTERNAL void PaintScaleUniform::closurev1 (hb_colrv1_closure_context_t* c) const +{ (this+src).dispatch (c); } + +HB_INTERNAL void PaintScaleUniformAroundCenter::closurev1 (hb_colrv1_closure_context_t* c) const +{ (this+src).dispatch (c); } + +HB_INTERNAL void PaintRotate::closurev1 (hb_colrv1_closure_context_t* c) const +{ (this+src).dispatch (c); } + +HB_INTERNAL void PaintRotateAroundCenter::closurev1 (hb_colrv1_closure_context_t* c) const +{ (this+src).dispatch (c); } + +HB_INTERNAL void PaintSkew::closurev1 (hb_colrv1_closure_context_t* c) const +{ (this+src).dispatch (c); } + +HB_INTERNAL void PaintSkewAroundCenter::closurev1 (hb_colrv1_closure_context_t* c) const +{ (this+src).dispatch (c); } + +HB_INTERNAL void PaintComposite::closurev1 (hb_colrv1_closure_context_t* c) const +{ + (this+src).dispatch (c); + (this+backdrop).dispatch (c); +} + +} /* namespace OT */ + + +#endif /* OT_COLOR_COLR_COLRV1_CLOSURE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Color/CPAL/CPAL.hh b/src/3rdparty/harfbuzz-ng/src/OT/Color/CPAL/CPAL.hh new file mode 100644 index 00000000000..c07716c1c98 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Color/CPAL/CPAL.hh @@ -0,0 +1,350 @@ +/* + * Copyright © 2016 Google, Inc. + * Copyright © 2018 Ebrahim Byagowi + * + * 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): Sascha Brawer + */ + +#ifndef OT_COLOR_CPAL_CPAL_HH +#define OT_COLOR_CPAL_CPAL_HH + +#include "../../../hb-open-type.hh" +#include "../../../hb-ot-color.h" +#include "../../../hb-ot-name.h" + + +/* + * CPAL -- Color Palette + * https://docs.microsoft.com/en-us/typography/opentype/spec/cpal + */ +#define HB_OT_TAG_CPAL HB_TAG('C','P','A','L') + +namespace OT { + + +struct CPALV1Tail +{ + friend struct CPAL; + + private: + hb_ot_color_palette_flags_t get_palette_flags (const void *base, + unsigned int palette_index, + unsigned int palette_count) const + { + if (!paletteFlagsZ) return HB_OT_COLOR_PALETTE_FLAG_DEFAULT; + return (hb_ot_color_palette_flags_t) (uint32_t) + (base+paletteFlagsZ).as_array (palette_count)[palette_index]; + } + + hb_ot_name_id_t get_palette_name_id (const void *base, + unsigned int palette_index, + unsigned int palette_count) const + { + if (!paletteLabelsZ) return HB_OT_NAME_ID_INVALID; + return (base+paletteLabelsZ).as_array (palette_count)[palette_index]; + } + + hb_ot_name_id_t get_color_name_id (const void *base, + unsigned int color_index, + unsigned int color_count) const + { + if (!colorLabelsZ) return HB_OT_NAME_ID_INVALID; + return (base+colorLabelsZ).as_array (color_count)[color_index]; + } + + public: + void collect_name_ids (const void *base, + unsigned palette_count, + unsigned color_count, + const hb_map_t *color_index_map, + hb_set_t *nameids_to_retain /* OUT */) const + { + if (paletteLabelsZ) + { + + (base+paletteLabelsZ).as_array (palette_count) + | hb_sink (nameids_to_retain) + ; + } + + if (colorLabelsZ) + { + const hb_array_t colorLabels = (base+colorLabelsZ).as_array (color_count); + for (unsigned i = 0; i < color_count; i++) + { + if (!color_index_map->has (i)) continue; + nameids_to_retain->add (colorLabels[i]); + } + } + } + + bool serialize (hb_serialize_context_t *c, + unsigned palette_count, + unsigned color_count, + const void *base, + const hb_map_t *color_index_map) const + { + TRACE_SERIALIZE (this); + auto *out = c->allocate_size (static_size); + if (unlikely (!out)) return_trace (false); + + out->paletteFlagsZ = 0; + if (paletteFlagsZ) + out->paletteFlagsZ.serialize_copy (c, paletteFlagsZ, base, 0, hb_serialize_context_t::Head, palette_count); + + out->paletteLabelsZ = 0; + if (paletteLabelsZ) + out->paletteLabelsZ.serialize_copy (c, paletteLabelsZ, base, 0, hb_serialize_context_t::Head, palette_count); + + const hb_array_t colorLabels = (base+colorLabelsZ).as_array (color_count); + if (colorLabelsZ) + { + c->push (); + for (unsigned i = 0; i < color_count; i++) + { + if (!color_index_map->has (i)) continue; + if (!c->copy (colorLabels[i])) + { + c->pop_discard (); + return_trace (false); + } + } + c->add_link (out->colorLabelsZ, c->pop_pack ()); + } + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c, + const void *base, + unsigned int palette_count, + unsigned int color_count) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + (!paletteFlagsZ || (base+paletteFlagsZ).sanitize (c, palette_count)) && + (!paletteLabelsZ || (base+paletteLabelsZ).sanitize (c, palette_count)) && + (!colorLabelsZ || (base+colorLabelsZ).sanitize (c, color_count))); + } + + protected: + // TODO(garretrieger): these offsets can hold nulls so we should not be using non-null offsets + // here. Currently they are needed since UnsizedArrayOf doesn't define null_size + NNOffset32To> + paletteFlagsZ; /* Offset from the beginning of CPAL table to + * the Palette Type Array. Set to 0 if no array + * is provided. */ + NNOffset32To> + paletteLabelsZ; /* Offset from the beginning of CPAL table to + * the palette labels array. Set to 0 if no + * array is provided. */ + NNOffset32To> + colorLabelsZ; /* Offset from the beginning of CPAL table to + * the color labels array. Set to 0 + * if no array is provided. */ + public: + DEFINE_SIZE_STATIC (12); +}; + +typedef HBUINT32 BGRAColor; + +struct CPAL +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_CPAL; + + bool has_data () const { return numPalettes; } + + unsigned int get_size () const + { return min_size + numPalettes * sizeof (colorRecordIndicesZ[0]); } + + unsigned int get_palette_count () const { return numPalettes; } + unsigned int get_color_count () const { return numColors; } + + hb_ot_color_palette_flags_t get_palette_flags (unsigned int palette_index) const + { return v1 ().get_palette_flags (this, palette_index, numPalettes); } + + hb_ot_name_id_t get_palette_name_id (unsigned int palette_index) const + { return v1 ().get_palette_name_id (this, palette_index, numPalettes); } + + hb_ot_name_id_t get_color_name_id (unsigned int color_index) const + { return v1 ().get_color_name_id (this, color_index, numColors); } + + unsigned int get_palette_colors (unsigned int palette_index, + unsigned int start_offset, + unsigned int *color_count, /* IN/OUT. May be NULL. */ + hb_color_t *colors /* OUT. May be NULL. */) const + { + if (unlikely (palette_index >= numPalettes)) + { + if (color_count) *color_count = 0; + return 0; + } + unsigned int start_index = colorRecordIndicesZ[palette_index]; + hb_array_t all_colors ((this+colorRecordsZ).arrayZ, numColorRecords); + hb_array_t palette_colors = all_colors.sub_array (start_index, + numColors); + if (color_count) + { + + palette_colors.sub_array (start_offset, color_count) + | hb_sink (hb_array (colors, *color_count)) + ; + } + return numColors; + } + + void collect_name_ids (const hb_map_t *color_index_map, + hb_set_t *nameids_to_retain /* OUT */) const + { + if (version == 1) + v1 ().collect_name_ids (this, numPalettes, numColors, color_index_map, nameids_to_retain); + } + + private: + const CPALV1Tail& v1 () const + { + if (version == 0) return Null (CPALV1Tail); + return StructAfter (*this); + } + + public: + bool serialize (hb_serialize_context_t *c, + const hb_array_t &color_record_indices, + const hb_array_t &color_records, + const hb_vector_t& first_color_index_for_layer, + const hb_map_t& first_color_to_layer_index, + const hb_set_t &retained_color_indices) const + { + TRACE_SERIALIZE (this); + + // TODO(grieger): limit total final size. + + for (const auto idx : color_record_indices) + { + hb_codepoint_t layer_index = first_color_to_layer_index[idx]; + + HBUINT16 new_idx; + new_idx = layer_index * retained_color_indices.get_population (); + if (!c->copy (new_idx)) return_trace (false); + } + + c->push (); + for (unsigned first_color_index : first_color_index_for_layer) + { + for (hb_codepoint_t color_index : retained_color_indices) + { + if (!c->copy (color_records[first_color_index + color_index])) + { + c->pop_discard (); + return_trace (false); + } + } + } + + c->add_link (colorRecordsZ, c->pop_pack ()); + return_trace (true); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + if (!numPalettes) return_trace (false); + + const hb_map_t *color_index_map = &c->plan->colr_palettes; + if (color_index_map->is_empty ()) return_trace (false); + + hb_set_t retained_color_indices; + for (const auto _ : color_index_map->keys ()) + { + if (_ == 0xFFFF) continue; + retained_color_indices.add (_); + } + if (retained_color_indices.is_empty ()) return_trace (false); + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + + out->version = version; + out->numColors = retained_color_indices.get_population (); + out->numPalettes = numPalettes; + + hb_vector_t first_color_index_for_layer; + hb_map_t first_color_to_layer_index; + + const hb_array_t colorRecordIndices = colorRecordIndicesZ.as_array (numPalettes); + for (const auto first_color_record_idx : colorRecordIndices) + { + if (first_color_to_layer_index.has (first_color_record_idx)) continue; + + first_color_index_for_layer.push (first_color_record_idx); + first_color_to_layer_index.set (first_color_record_idx, + first_color_index_for_layer.length - 1); + } + + out->numColorRecords = first_color_index_for_layer.length + * retained_color_indices.get_population (); + + const hb_array_t color_records = (this+colorRecordsZ).as_array (numColorRecords); + if (!out->serialize (c->serializer, + colorRecordIndices, + color_records, + first_color_index_for_layer, + first_color_to_layer_index, + retained_color_indices)) + return_trace (false); + + if (version == 1) + return_trace (v1 ().serialize (c->serializer, numPalettes, numColors, this, color_index_map)); + + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + (this+colorRecordsZ).sanitize (c, numColorRecords) && + colorRecordIndicesZ.sanitize (c, numPalettes) && + (version == 0 || v1 ().sanitize (c, this, numPalettes, numColors))); + } + + protected: + HBUINT16 version; /* Table version number */ + /* Version 0 */ + HBUINT16 numColors; /* Number of colors in each palette. */ + HBUINT16 numPalettes; /* Number of palettes in the table. */ + HBUINT16 numColorRecords; /* Total number of color records, combined for + * all palettes. */ + NNOffset32To> + colorRecordsZ; /* Offset from the beginning of CPAL table to + * the first ColorRecord. */ + UnsizedArrayOf + colorRecordIndicesZ; /* Index of each palette’s first color record in + * the combined color record array. */ +/*CPALV1Tail v1;*/ + public: + DEFINE_SIZE_ARRAY (12, colorRecordIndicesZ); +}; + +} /* namespace OT */ + + +#endif /* OT_COLOR_CPAL_CPAL_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Color/sbix/sbix.hh b/src/3rdparty/harfbuzz-ng/src/OT/Color/sbix/sbix.hh new file mode 100644 index 00000000000..ce8693cfb16 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Color/sbix/sbix.hh @@ -0,0 +1,447 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * Copyright © 2020 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): Calder Kitagawa + */ + +#ifndef OT_COLOR_SBIX_SBIX_HH +#define OT_COLOR_SBIX_SBIX_HH + +#include "../../../hb-open-type.hh" +#include "../../../hb-paint.hh" + +/* + * sbix -- Standard Bitmap Graphics + * https://docs.microsoft.com/en-us/typography/opentype/spec/sbix + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6sbix.html + */ +#define HB_OT_TAG_sbix HB_TAG('s','b','i','x') + + +namespace OT { + + +struct SBIXGlyph +{ + SBIXGlyph* copy (hb_serialize_context_t *c, unsigned int data_length) const + { + TRACE_SERIALIZE (this); + SBIXGlyph* new_glyph = c->start_embed (); + if (unlikely (!c->extend_min (new_glyph))) return_trace (nullptr); + + new_glyph->xOffset = xOffset; + new_glyph->yOffset = yOffset; + new_glyph->graphicType = graphicType; + data.copy (c, data_length); + return_trace (new_glyph); + } + + HBINT16 xOffset; /* The horizontal (x-axis) offset from the left + * edge of the graphic to the glyph’s origin. + * That is, the x-coordinate of the point on the + * baseline at the left edge of the glyph. */ + HBINT16 yOffset; /* The vertical (y-axis) offset from the bottom + * edge of the graphic to the glyph’s origin. + * That is, the y-coordinate of the point on the + * baseline at the left edge of the glyph. */ + Tag graphicType; /* Indicates the format of the embedded graphic + * data: one of 'jpg ', 'png ' or 'tiff', or the + * special format 'dupe'. */ + UnsizedArrayOf + data; /* The actual embedded graphic data. The total + * length is inferred from sequential entries in + * the glyphDataOffsets array and the fixed size + * (8 bytes) of the preceding fields. */ + public: + DEFINE_SIZE_ARRAY (8, data); +}; + +struct SBIXStrike +{ + static unsigned int get_size (unsigned num_glyphs) + { return min_size + num_glyphs * HBUINT32::static_size; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + imageOffsetsZ.sanitize_shallow (c, c->get_num_glyphs () + 1)); + } + + hb_blob_t *get_glyph_blob (unsigned int glyph_id, + hb_blob_t *sbix_blob, + hb_tag_t file_type, + int *x_offset, + int *y_offset, + unsigned int num_glyphs, + unsigned int *strike_ppem) const + { + if (unlikely (!ppem)) return hb_blob_get_empty (); /* To get Null() object out of the way. */ + + unsigned int retry_count = 8; + unsigned int sbix_len = sbix_blob->length; + unsigned int strike_offset = (const char *) this - (const char *) sbix_blob->data; + assert (strike_offset < sbix_len); + + retry: + if (unlikely (glyph_id >= num_glyphs || + imageOffsetsZ[glyph_id + 1] <= imageOffsetsZ[glyph_id] || + imageOffsetsZ[glyph_id + 1] - imageOffsetsZ[glyph_id] <= SBIXGlyph::min_size || + (unsigned int) imageOffsetsZ[glyph_id + 1] > sbix_len - strike_offset)) + return hb_blob_get_empty (); + + unsigned int glyph_offset = strike_offset + (unsigned int) imageOffsetsZ[glyph_id] + SBIXGlyph::min_size; + unsigned int glyph_length = imageOffsetsZ[glyph_id + 1] - imageOffsetsZ[glyph_id] - SBIXGlyph::min_size; + + const SBIXGlyph *glyph = &(this+imageOffsetsZ[glyph_id]); + + if (glyph->graphicType == HB_TAG ('d','u','p','e')) + { + if (glyph_length >= 2) + { + glyph_id = *((HBUINT16 *) &glyph->data); + if (retry_count--) + goto retry; + } + return hb_blob_get_empty (); + } + + if (unlikely (file_type != glyph->graphicType)) + return hb_blob_get_empty (); + + if (strike_ppem) *strike_ppem = ppem; + if (x_offset) *x_offset = glyph->xOffset; + if (y_offset) *y_offset = glyph->yOffset; + return hb_blob_create_sub_blob (sbix_blob, glyph_offset, glyph_length); + } + + bool subset (hb_subset_context_t *c, unsigned int available_len) const + { + TRACE_SUBSET (this); + unsigned int num_output_glyphs = c->plan->num_output_glyphs (); + + auto* out = c->serializer->start_embed (); + auto snap = c->serializer->snapshot (); + if (unlikely (!c->serializer->extend (out, num_output_glyphs + 1))) return_trace (false); + out->ppem = ppem; + out->resolution = resolution; + HBUINT32 head; + head = get_size (num_output_glyphs + 1); + + bool has_glyphs = false; + for (unsigned new_gid = 0; new_gid < num_output_glyphs; new_gid++) + { + hb_codepoint_t old_gid; + if (!c->plan->old_gid_for_new_gid (new_gid, &old_gid) || + unlikely (imageOffsetsZ[old_gid].is_null () || + imageOffsetsZ[old_gid + 1].is_null () || + imageOffsetsZ[old_gid + 1] <= imageOffsetsZ[old_gid] || + imageOffsetsZ[old_gid + 1] - imageOffsetsZ[old_gid] <= SBIXGlyph::min_size) || + (unsigned int) imageOffsetsZ[old_gid + 1] > available_len) + { + out->imageOffsetsZ[new_gid] = head; + continue; + } + has_glyphs = true; + unsigned int delta = imageOffsetsZ[old_gid + 1] - imageOffsetsZ[old_gid]; + unsigned int glyph_data_length = delta - SBIXGlyph::min_size; + if (!(this+imageOffsetsZ[old_gid]).copy (c->serializer, glyph_data_length)) + return_trace (false); + out->imageOffsetsZ[new_gid] = head; + head += delta; + } + if (has_glyphs) + out->imageOffsetsZ[num_output_glyphs] = head; + else + c->serializer->revert (snap); + return_trace (has_glyphs); + } + + public: + HBUINT16 ppem; /* The PPEM size for which this strike was designed. */ + HBUINT16 resolution; /* The device pixel density (in PPI) for which this + * strike was designed. (E.g., 96 PPI, 192 PPI.) */ + protected: + UnsizedArrayOf> + imageOffsetsZ; /* Offset from the beginning of the strike data header + * to bitmap data for an individual glyph ID. */ + public: + DEFINE_SIZE_ARRAY (4, imageOffsetsZ); +}; + +struct sbix +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_sbix; + + bool has_data () const { return version; } + + const SBIXStrike &get_strike (unsigned int i) const { return this+strikes[i]; } + + struct accelerator_t + { + accelerator_t (hb_face_t *face) + { + table = hb_sanitize_context_t ().reference_table (face); + num_glyphs = face->get_num_glyphs (); + } + ~accelerator_t () { table.destroy (); } + + bool has_data () const { return table->has_data (); } + + bool get_extents (hb_font_t *font, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents, + bool scale = true) const + { + /* We only support PNG right now, and following function checks type. */ + return get_png_extents (font, glyph, extents, scale); + } + + hb_blob_t *reference_png (hb_font_t *font, + hb_codepoint_t glyph_id, + int *x_offset, + int *y_offset, + unsigned int *available_ppem) const + { + return choose_strike (font).get_glyph_blob (glyph_id, table.get_blob (), + HB_TAG ('p','n','g',' '), + x_offset, y_offset, + num_glyphs, available_ppem); + } + + bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data) const + { + if (!has_data ()) + return false; + + int x_offset = 0, y_offset = 0; + unsigned int strike_ppem = 0; + hb_blob_t *blob = reference_png (font, glyph, &x_offset, &y_offset, &strike_ppem); + hb_glyph_extents_t extents; + hb_glyph_extents_t pixel_extents; + + if (blob == hb_blob_get_empty ()) + return false; + + if (!hb_font_get_glyph_extents (font, glyph, &extents)) + return false; + + if (unlikely (!get_extents (font, glyph, &pixel_extents, false))) + return false; + + bool ret = funcs->image (data, + blob, + pixel_extents.width, -pixel_extents.height, + HB_PAINT_IMAGE_FORMAT_PNG, + font->slant_xy, + &extents); + + hb_blob_destroy (blob); + return ret; + } + + private: + + const SBIXStrike &choose_strike (hb_font_t *font) const + { + unsigned count = table->strikes.len; + if (unlikely (!count)) + return Null (SBIXStrike); + + unsigned int requested_ppem = hb_max (font->x_ppem, font->y_ppem); + if (!requested_ppem) + requested_ppem = 1<<30; /* Choose largest strike. */ + /* TODO Add DPI sensitivity as well? */ + unsigned int best_i = 0; + unsigned int best_ppem = table->get_strike (0).ppem; + + for (unsigned int i = 1; i < count; i++) + { + unsigned int ppem = (table->get_strike (i)).ppem; + if ((requested_ppem <= ppem && ppem < best_ppem) || + (requested_ppem > best_ppem && ppem > best_ppem)) + { + best_i = i; + best_ppem = ppem; + } + } + + return table->get_strike (best_i); + } + + struct PNGHeader + { + HBUINT8 signature[8]; + struct + { + struct + { + HBUINT32 length; + Tag type; + } header; + HBUINT32 width; + HBUINT32 height; + HBUINT8 bitDepth; + HBUINT8 colorType; + HBUINT8 compressionMethod; + HBUINT8 filterMethod; + HBUINT8 interlaceMethod; + } IHDR; + + public: + DEFINE_SIZE_STATIC (29); + }; + + bool get_png_extents (hb_font_t *font, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents, + bool scale = true) const + { + /* Following code is safe to call even without data. + * But faster to short-circuit. */ + if (!has_data ()) + return false; + + int x_offset = 0, y_offset = 0; + unsigned int strike_ppem = 0; + hb_blob_t *blob = reference_png (font, glyph, &x_offset, &y_offset, &strike_ppem); + + const PNGHeader &png = *blob->as(); + + if (png.IHDR.height >= 65536 || png.IHDR.width >= 65536) + { + hb_blob_destroy (blob); + return false; + } + + extents->x_bearing = x_offset; + extents->y_bearing = png.IHDR.height + y_offset; + extents->width = png.IHDR.width; + extents->height = -1 * png.IHDR.height; + + /* Convert to font units. */ + if (strike_ppem && scale) + { + float scale = font->face->get_upem () / (float) strike_ppem; + extents->x_bearing = roundf (extents->x_bearing * scale); + extents->y_bearing = roundf (extents->y_bearing * scale); + extents->width = roundf (extents->width * scale); + extents->height = roundf (extents->height * scale); + } + + if (scale) + font->scale_glyph_extents (extents); + + hb_blob_destroy (blob); + + return strike_ppem; + } + + private: + hb_blob_ptr_t table; + + unsigned int num_glyphs; + }; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + version >= 1 && + strikes.sanitize (c, this))); + } + + bool + add_strike (hb_subset_context_t *c, unsigned i) const + { + if (strikes[i].is_null () || c->source_blob->length < (unsigned) strikes[i]) + return false; + + return (this+strikes[i]).subset (c, c->source_blob->length - (unsigned) strikes[i]); + } + + bool serialize_strike_offsets (hb_subset_context_t *c) const + { + TRACE_SERIALIZE (this); + + auto *out = c->serializer->start_embed> (); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + hb_vector_t*> new_strikes; + hb_vector_t objidxs; + for (int i = strikes.len - 1; i >= 0; --i) + { + auto* o = out->serialize_append (c->serializer); + if (unlikely (!o)) return_trace (false); + *o = 0; + auto snap = c->serializer->snapshot (); + c->serializer->push (); + bool ret = add_strike (c, i); + if (!ret) + { + c->serializer->pop_discard (); + out->pop (); + c->serializer->revert (snap); + } + else + { + objidxs.push (c->serializer->pop_pack ()); + new_strikes.push (o); + } + } + for (unsigned int i = 0; i < new_strikes.length; ++i) + c->serializer->add_link (*new_strikes[i], objidxs[new_strikes.length - 1 - i]); + + return_trace (true); + } + + bool subset (hb_subset_context_t* c) const + { + TRACE_SUBSET (this); + + if (unlikely (!c->serializer->embed (this->version))) return_trace (false); + if (unlikely (!c->serializer->embed (this->flags))) return_trace (false); + + return_trace (serialize_strike_offsets (c)); + } + + protected: + HBUINT16 version; /* Table version number — set to 1 */ + HBUINT16 flags; /* Bit 0: Set to 1. Bit 1: Draw outlines. + * Bits 2 to 15: reserved (set to 0). */ + Array32OfOffset32To + strikes; /* Offsets from the beginning of the 'sbix' + * table to data for each individual bitmap strike. */ + public: + DEFINE_SIZE_ARRAY (8, strikes); +}; + +struct sbix_accelerator_t : sbix::accelerator_t { + sbix_accelerator_t (hb_face_t *face) : sbix::accelerator_t (face) {} +}; + + +} /* namespace OT */ + +#endif /* OT_COLOR_SBIX_SBIX_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Color/svg/svg.hh b/src/3rdparty/harfbuzz-ng/src/OT/Color/svg/svg.hh new file mode 100644 index 00000000000..c7d91b88ee0 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Color/svg/svg.hh @@ -0,0 +1,151 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * + * 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. + */ + +#ifndef OT_COLOR_SVG_SVG_HH +#define OT_COLOR_SVG_SVG_HH + +#include "../../../hb-open-type.hh" +#include "../../../hb-blob.hh" +#include "../../../hb-paint.hh" + +/* + * SVG -- SVG (Scalable Vector Graphics) + * https://docs.microsoft.com/en-us/typography/opentype/spec/svg + */ + +#define HB_OT_TAG_SVG HB_TAG('S','V','G',' ') + + +namespace OT { + + +struct SVGDocumentIndexEntry +{ + int cmp (hb_codepoint_t g) const + { return g < startGlyphID ? -1 : g > endGlyphID ? 1 : 0; } + + hb_blob_t *reference_blob (hb_blob_t *svg_blob, unsigned int index_offset) const + { + return hb_blob_create_sub_blob (svg_blob, + index_offset + (unsigned int) svgDoc, + svgDocLength); + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + svgDoc.sanitize (c, base, svgDocLength)); + } + + protected: + HBUINT16 startGlyphID; /* The first glyph ID in the range described by + * this index entry. */ + HBUINT16 endGlyphID; /* The last glyph ID in the range described by + * this index entry. Must be >= startGlyphID. */ + NNOffset32To> + svgDoc; /* Offset from the beginning of the SVG Document Index + * to an SVG document. Must be non-zero. */ + HBUINT32 svgDocLength; /* Length of the SVG document. + * Must be non-zero. */ + public: + DEFINE_SIZE_STATIC (12); +}; + +struct SVG +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_SVG; + + bool has_data () const { return svgDocEntries; } + + struct accelerator_t + { + accelerator_t (hb_face_t *face) + { table = hb_sanitize_context_t ().reference_table (face); } + ~accelerator_t () { table.destroy (); } + + hb_blob_t *reference_blob_for_glyph (hb_codepoint_t glyph_id) const + { + return table->get_glyph_entry (glyph_id).reference_blob (table.get_blob (), + table->svgDocEntries); + } + + bool has_data () const { return table->has_data (); } + + bool paint_glyph (hb_font_t *font HB_UNUSED, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data) const + { + if (!has_data ()) + return false; + + hb_blob_t *blob = reference_blob_for_glyph (glyph); + + if (blob == hb_blob_get_empty ()) + return false; + + funcs->image (data, + blob, + 0, 0, + HB_PAINT_IMAGE_FORMAT_SVG, + font->slant_xy, + nullptr); + + hb_blob_destroy (blob); + return true; + } + + private: + hb_blob_ptr_t table; + public: + DEFINE_SIZE_STATIC (sizeof (hb_blob_ptr_t)); + }; + + const SVGDocumentIndexEntry &get_glyph_entry (hb_codepoint_t glyph_id) const + { return (this+svgDocEntries).bsearch (glyph_id); } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + (this+svgDocEntries).sanitize_shallow (c))); + } + + protected: + HBUINT16 version; /* Table version (starting at 0). */ + Offset32To> + svgDocEntries; /* Offset (relative to the start of the SVG table) to the + * SVG Documents Index. Must be non-zero. */ + /* Array of SVG Document Index Entries. */ + HBUINT32 reserved; /* Set to 0. */ + public: + DEFINE_SIZE_STATIC (10); +}; + +struct SVG_accelerator_t : SVG::accelerator_t { + SVG_accelerator_t (hb_face_t *face) : SVG::accelerator_t (face) {} +}; + +} /* namespace OT */ + + +#endif /* OT_COLOR_SVG_SVG_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/Common/Coverage.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/Common/Coverage.hh new file mode 100644 index 00000000000..25056c9bc35 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/Common/Coverage.hh @@ -0,0 +1,351 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2010,2012 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod, Garret Rieger + */ + +#ifndef OT_LAYOUT_COMMON_COVERAGE_HH +#define OT_LAYOUT_COMMON_COVERAGE_HH + +#include "../types.hh" +#include "CoverageFormat1.hh" +#include "CoverageFormat2.hh" + +namespace OT { +namespace Layout { +namespace Common { + +template +static inline void Coverage_serialize (hb_serialize_context_t *c, + Iterator it); + +struct Coverage +{ + + protected: + union { + HBUINT16 format; /* Format identifier */ + CoverageFormat1_3 format1; + CoverageFormat2_4 format2; +#ifndef HB_NO_BEYOND_64K + CoverageFormat1_3format3; + CoverageFormat2_4format4; +#endif + } u; + public: + DEFINE_SIZE_UNION (2, format); + +#ifndef HB_OPTIMIZE_SIZE + HB_ALWAYS_INLINE +#endif + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.format.sanitize (c)) return_trace (false); + switch (u.format) + { + case 1: return_trace (u.format1.sanitize (c)); + case 2: return_trace (u.format2.sanitize (c)); +#ifndef HB_NO_BEYOND_64K + case 3: return_trace (u.format3.sanitize (c)); + case 4: return_trace (u.format4.sanitize (c)); +#endif + default:return_trace (true); + } + } + + /* Has interface. */ + unsigned operator [] (hb_codepoint_t k) const { return get (k); } + bool has (hb_codepoint_t k) const { return (*this)[k] != NOT_COVERED; } + /* Predicate. */ + bool operator () (hb_codepoint_t k) const { return has (k); } + + unsigned int get (hb_codepoint_t k) const { return get_coverage (k); } + unsigned int get_coverage (hb_codepoint_t glyph_id) const + { + switch (u.format) { + case 1: return u.format1.get_coverage (glyph_id); + case 2: return u.format2.get_coverage (glyph_id); +#ifndef HB_NO_BEYOND_64K + case 3: return u.format3.get_coverage (glyph_id); + case 4: return u.format4.get_coverage (glyph_id); +#endif + default:return NOT_COVERED; + } + } + + unsigned get_population () const + { + switch (u.format) { + case 1: return u.format1.get_population (); + case 2: return u.format2.get_population (); +#ifndef HB_NO_BEYOND_64K + case 3: return u.format3.get_population (); + case 4: return u.format4.get_population (); +#endif + default:return NOT_COVERED; + } + } + + template + bool serialize (hb_serialize_context_t *c, Iterator glyphs) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (this))) return_trace (false); + + unsigned count = hb_len (glyphs); + unsigned num_ranges = 0; + hb_codepoint_t last = (hb_codepoint_t) -2; + hb_codepoint_t max = 0; + bool unsorted = false; + for (auto g: glyphs) + { + if (last != (hb_codepoint_t) -2 && g < last) + unsorted = true; + if (last + 1 != g) + num_ranges++; + last = g; + if (g > max) max = g; + } + u.format = !unsorted && count <= num_ranges * 3 ? 1 : 2; + +#ifndef HB_NO_BEYOND_64K + if (max > 0xFFFFu) + u.format += 2; + if (unlikely (max > 0xFFFFFFu)) +#else + if (unlikely (max > 0xFFFFu)) +#endif + { + c->check_success (false, HB_SERIALIZE_ERROR_INT_OVERFLOW); + return_trace (false); + } + + switch (u.format) + { + case 1: return_trace (u.format1.serialize (c, glyphs)); + case 2: return_trace (u.format2.serialize (c, glyphs)); +#ifndef HB_NO_BEYOND_64K + case 3: return_trace (u.format3.serialize (c, glyphs)); + case 4: return_trace (u.format4.serialize (c, glyphs)); +#endif + default:return_trace (false); + } + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto it = + + iter () + | hb_take (c->plan->source->get_num_glyphs ()) + | hb_map_retains_sorting (c->plan->glyph_map_gsub) + | hb_filter ([] (hb_codepoint_t glyph) { return glyph != HB_MAP_VALUE_INVALID; }) + ; + + // Cache the iterator result as it will be iterated multiple times + // by the serialize code below. + hb_sorted_vector_t glyphs (it); + Coverage_serialize (c->serializer, glyphs.iter ()); + return_trace (bool (glyphs)); + } + + bool intersects (const hb_set_t *glyphs) const + { + switch (u.format) + { + case 1: return u.format1.intersects (glyphs); + case 2: return u.format2.intersects (glyphs); +#ifndef HB_NO_BEYOND_64K + case 3: return u.format3.intersects (glyphs); + case 4: return u.format4.intersects (glyphs); +#endif + default:return false; + } + } + bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const + { + switch (u.format) + { + case 1: return u.format1.intersects_coverage (glyphs, index); + case 2: return u.format2.intersects_coverage (glyphs, index); +#ifndef HB_NO_BEYOND_64K + case 3: return u.format3.intersects_coverage (glyphs, index); + case 4: return u.format4.intersects_coverage (glyphs, index); +#endif + default:return false; + } + } + + /* Might return false if array looks unsorted. + * Used for faster rejection of corrupt data. */ + template + bool collect_coverage (set_t *glyphs) const + { + switch (u.format) + { + case 1: return u.format1.collect_coverage (glyphs); + case 2: return u.format2.collect_coverage (glyphs); +#ifndef HB_NO_BEYOND_64K + case 3: return u.format3.collect_coverage (glyphs); + case 4: return u.format4.collect_coverage (glyphs); +#endif + default:return false; + } + } + + template + void intersect_set (const hb_set_t &glyphs, IterableOut&& intersect_glyphs) const + { + switch (u.format) + { + case 1: return u.format1.intersect_set (glyphs, intersect_glyphs); + case 2: return u.format2.intersect_set (glyphs, intersect_glyphs); +#ifndef HB_NO_BEYOND_64K + case 3: return u.format3.intersect_set (glyphs, intersect_glyphs); + case 4: return u.format4.intersect_set (glyphs, intersect_glyphs); +#endif + default:return ; + } + } + + struct iter_t : hb_iter_with_fallback_t + { + static constexpr bool is_sorted_iterator = true; + iter_t (const Coverage &c_ = Null (Coverage)) + { + hb_memset (this, 0, sizeof (*this)); + format = c_.u.format; + switch (format) + { + case 1: u.format1.init (c_.u.format1); return; + case 2: u.format2.init (c_.u.format2); return; +#ifndef HB_NO_BEYOND_64K + case 3: u.format3.init (c_.u.format3); return; + case 4: u.format4.init (c_.u.format4); return; +#endif + default: return; + } + } + bool __more__ () const + { + switch (format) + { + case 1: return u.format1.__more__ (); + case 2: return u.format2.__more__ (); +#ifndef HB_NO_BEYOND_64K + case 3: return u.format3.__more__ (); + case 4: return u.format4.__more__ (); +#endif + default:return false; + } + } + void __next__ () + { + switch (format) + { + case 1: u.format1.__next__ (); break; + case 2: u.format2.__next__ (); break; +#ifndef HB_NO_BEYOND_64K + case 3: u.format3.__next__ (); break; + case 4: u.format4.__next__ (); break; +#endif + default: break; + } + } + typedef hb_codepoint_t __item_t__; + __item_t__ __item__ () const { return get_glyph (); } + + hb_codepoint_t get_glyph () const + { + switch (format) + { + case 1: return u.format1.get_glyph (); + case 2: return u.format2.get_glyph (); +#ifndef HB_NO_BEYOND_64K + case 3: return u.format3.get_glyph (); + case 4: return u.format4.get_glyph (); +#endif + default:return 0; + } + } + bool operator != (const iter_t& o) const + { + if (unlikely (format != o.format)) return true; + switch (format) + { + case 1: return u.format1 != o.u.format1; + case 2: return u.format2 != o.u.format2; +#ifndef HB_NO_BEYOND_64K + case 3: return u.format3 != o.u.format3; + case 4: return u.format4 != o.u.format4; +#endif + default:return false; + } + } + iter_t __end__ () const + { + iter_t it = {}; + it.format = format; + switch (format) + { + case 1: it.u.format1 = u.format1.__end__ (); break; + case 2: it.u.format2 = u.format2.__end__ (); break; +#ifndef HB_NO_BEYOND_64K + case 3: it.u.format3 = u.format3.__end__ (); break; + case 4: it.u.format4 = u.format4.__end__ (); break; +#endif + default: break; + } + return it; + } + + private: + unsigned int format; + union { +#ifndef HB_NO_BEYOND_64K + CoverageFormat2_4::iter_t format4; /* Put this one first since it's larger; helps shut up compiler. */ + CoverageFormat1_3::iter_t format3; +#endif + CoverageFormat2_4::iter_t format2; /* Put this one first since it's larger; helps shut up compiler. */ + CoverageFormat1_3::iter_t format1; + } u; + }; + iter_t iter () const { return iter_t (*this); } +}; + +template +static inline void +Coverage_serialize (hb_serialize_context_t *c, + Iterator it) +{ c->start_embed ()->serialize (c, it); } + +} +} +} + +#endif // #ifndef OT_LAYOUT_COMMON_COVERAGE_HH diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/Common/CoverageFormat1.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/Common/CoverageFormat1.hh new file mode 100644 index 00000000000..3f598d40efc --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/Common/CoverageFormat1.hh @@ -0,0 +1,133 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2010,2012 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod, Garret Rieger + */ + + +#ifndef OT_LAYOUT_COMMON_COVERAGEFORMAT1_HH +#define OT_LAYOUT_COMMON_COVERAGEFORMAT1_HH + +namespace OT { +namespace Layout { +namespace Common { + +#define NOT_COVERED ((unsigned int) -1) + +template +struct CoverageFormat1_3 +{ + friend struct Coverage; + + protected: + HBUINT16 coverageFormat; /* Format identifier--format = 1 */ + SortedArray16Of + glyphArray; /* Array of GlyphIDs--in numerical order */ + public: + DEFINE_SIZE_ARRAY (4, glyphArray); + + private: + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (glyphArray.sanitize (c)); + } + + unsigned int get_coverage (hb_codepoint_t glyph_id) const + { + unsigned int i; + glyphArray.bfind (glyph_id, &i, HB_NOT_FOUND_STORE, NOT_COVERED); + return i; + } + + unsigned get_population () const + { + return glyphArray.len; + } + + template + bool serialize (hb_serialize_context_t *c, Iterator glyphs) + { + TRACE_SERIALIZE (this); + return_trace (glyphArray.serialize (c, glyphs)); + } + + bool intersects (const hb_set_t *glyphs) const + { + if (glyphArray.len > glyphs->get_population () * hb_bit_storage ((unsigned) glyphArray.len) / 2) + { + for (auto g : *glyphs) + if (get_coverage (g) != NOT_COVERED) + return true; + return false; + } + + for (const auto& g : glyphArray.as_array ()) + if (glyphs->has (g)) + return true; + return false; + } + bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const + { return glyphs->has (glyphArray[index]); } + + template + void intersect_set (const hb_set_t &glyphs, IterableOut&& intersect_glyphs) const + { + unsigned count = glyphArray.len; + for (unsigned i = 0; i < count; i++) + if (glyphs.has (glyphArray[i])) + intersect_glyphs << glyphArray[i]; + } + + template + bool collect_coverage (set_t *glyphs) const + { return glyphs->add_sorted_array (glyphArray.as_array ()); } + + public: + /* Older compilers need this to be public. */ + struct iter_t + { + void init (const struct CoverageFormat1_3 &c_) { c = &c_; i = 0; } + bool __more__ () const { return i < c->glyphArray.len; } + void __next__ () { i++; } + hb_codepoint_t get_glyph () const { return c->glyphArray[i]; } + bool operator != (const iter_t& o) const + { return i != o.i; } + iter_t __end__ () const { iter_t it; it.init (*c); it.i = c->glyphArray.len; return it; } + + private: + const struct CoverageFormat1_3 *c; + unsigned int i; + }; + private: +}; + +} +} +} + +#endif // #ifndef OT_LAYOUT_COMMON_COVERAGEFORMAT1_HH diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/Common/CoverageFormat2.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/Common/CoverageFormat2.hh new file mode 100644 index 00000000000..9c875423568 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/Common/CoverageFormat2.hh @@ -0,0 +1,239 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2010,2012 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod, Garret Rieger + */ + +#ifndef OT_LAYOUT_COMMON_COVERAGEFORMAT2_HH +#define OT_LAYOUT_COMMON_COVERAGEFORMAT2_HH + +#include "RangeRecord.hh" + +namespace OT { +namespace Layout { +namespace Common { + +template +struct CoverageFormat2_4 +{ + friend struct Coverage; + + protected: + HBUINT16 coverageFormat; /* Format identifier--format = 2 */ + SortedArray16Of> + rangeRecord; /* Array of glyph ranges--ordered by + * Start GlyphID. rangeCount entries + * long */ + public: + DEFINE_SIZE_ARRAY (4, rangeRecord); + + private: + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (rangeRecord.sanitize (c)); + } + + unsigned int get_coverage (hb_codepoint_t glyph_id) const + { + const RangeRecord &range = rangeRecord.bsearch (glyph_id); + return likely (range.first <= range.last) + ? (unsigned int) range.value + (glyph_id - range.first) + : NOT_COVERED; + } + + unsigned get_population () const + { + typename Types::large_int ret = 0; + for (const auto &r : rangeRecord) + ret += r.get_population (); + return ret > UINT_MAX ? UINT_MAX : (unsigned) ret; + } + + template + bool serialize (hb_serialize_context_t *c, Iterator glyphs) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (this))) return_trace (false); + + unsigned num_ranges = 0; + hb_codepoint_t last = (hb_codepoint_t) -2; + for (auto g: glyphs) + { + if (last + 1 != g) + num_ranges++; + last = g; + } + + if (unlikely (!rangeRecord.serialize (c, num_ranges))) return_trace (false); + if (!num_ranges) return_trace (true); + + unsigned count = 0; + unsigned range = (unsigned) -1; + last = (hb_codepoint_t) -2; + unsigned unsorted = false; + for (auto g: glyphs) + { + if (last + 1 != g) + { + if (unlikely (last != (hb_codepoint_t) -2 && last + 1 > g)) + unsorted = true; + + range++; + rangeRecord.arrayZ[range].first = g; + rangeRecord.arrayZ[range].value = count; + } + rangeRecord.arrayZ[range].last = g; + last = g; + count++; + } + + if (unlikely (unsorted)) + rangeRecord.as_array ().qsort (RangeRecord::cmp_range); + + return_trace (true); + } + + bool intersects (const hb_set_t *glyphs) const + { + if (rangeRecord.len > glyphs->get_population () * hb_bit_storage ((unsigned) rangeRecord.len) / 2) + { + for (auto g : *glyphs) + if (get_coverage (g) != NOT_COVERED) + return true; + return false; + } + + return hb_any (+ hb_iter (rangeRecord) + | hb_map ([glyphs] (const RangeRecord &range) { return range.intersects (*glyphs); })); + } + bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const + { + auto *range = rangeRecord.as_array ().bsearch (index); + if (range) + return range->intersects (*glyphs); + return false; + } + + template + void intersect_set (const hb_set_t &glyphs, IterableOut&& intersect_glyphs) const + { + /* Break out of loop for overlapping, broken, tables, + * to avoid fuzzer timouts. */ + hb_codepoint_t last = 0; + for (const auto& range : rangeRecord) + { + if (unlikely (range.first < last)) + break; + last = range.last; + for (hb_codepoint_t g = range.first - 1; + glyphs.next (&g) && g <= last;) + intersect_glyphs << g; + } + } + + template + bool collect_coverage (set_t *glyphs) const + { + for (const auto& range: rangeRecord) + if (unlikely (!range.collect_coverage (glyphs))) + return false; + return true; + } + + public: + /* Older compilers need this to be public. */ + struct iter_t + { + void init (const CoverageFormat2_4 &c_) + { + c = &c_; + coverage = 0; + i = 0; + j = c->rangeRecord.len ? c->rangeRecord[0].first : 0; + if (unlikely (c->rangeRecord[0].first > c->rangeRecord[0].last)) + { + /* Broken table. Skip. */ + i = c->rangeRecord.len; + j = 0; + } + } + bool __more__ () const { return i < c->rangeRecord.len; } + void __next__ () + { + if (j >= c->rangeRecord[i].last) + { + i++; + if (__more__ ()) + { + unsigned int old = coverage; + j = c->rangeRecord.arrayZ[i].first; + coverage = c->rangeRecord.arrayZ[i].value; + if (unlikely (coverage != old + 1)) + { + /* Broken table. Skip. Important to avoid DoS. + * Also, our callers depend on coverage being + * consecutive and monotonically increasing, + * ie. iota(). */ + i = c->rangeRecord.len; + j = 0; + return; + } + } + else + j = 0; + return; + } + coverage++; + j++; + } + hb_codepoint_t get_glyph () const { return j; } + bool operator != (const iter_t& o) const + { return i != o.i || j != o.j; } + iter_t __end__ () const + { + iter_t it; + it.init (*c); + it.i = c->rangeRecord.len; + it.j = 0; + return it; + } + + private: + const struct CoverageFormat2_4 *c; + unsigned int i, coverage; + hb_codepoint_t j; + }; + private: +}; + +} +} +} + +#endif // #ifndef OT_LAYOUT_COMMON_COVERAGEFORMAT2_HH diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/Common/RangeRecord.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/Common/RangeRecord.hh new file mode 100644 index 00000000000..85aacace9a7 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/Common/RangeRecord.hh @@ -0,0 +1,97 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2010,2012 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod, Garret Rieger + */ + +#ifndef OT_LAYOUT_COMMON_RANGERECORD_HH +#define OT_LAYOUT_COMMON_RANGERECORD_HH + +namespace OT { +namespace Layout { +namespace Common { + +template +struct RangeRecord +{ + typename Types::HBGlyphID first; /* First GlyphID in the range */ + typename Types::HBGlyphID last; /* Last GlyphID in the range */ + HBUINT16 value; /* Value */ + + DEFINE_SIZE_STATIC (2 + 2 * Types::size); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + int cmp (hb_codepoint_t g) const + { return g < first ? -1 : g <= last ? 0 : +1; } + + HB_INTERNAL static int cmp_range (const void *pa, const void *pb) { + const RangeRecord *a = (const RangeRecord *) pa; + const RangeRecord *b = (const RangeRecord *) pb; + if (a->first < b->first) return -1; + if (a->first > b->first) return +1; + if (a->last < b->last) return -1; + if (a->last > b->last) return +1; + if (a->value < b->value) return -1; + if (a->value > b->value) return +1; + return 0; + } + + unsigned get_population () const + { + if (unlikely (last < first)) return 0; + return (last - first + 1); + } + + bool intersects (const hb_set_t &glyphs) const + { return glyphs.intersects (first, last); } + + template + bool collect_coverage (set_t *glyphs) const + { return glyphs->add_range (first, last); } +}; + +} +} +} + +// TODO(garretrieger): This was previously implemented using +// DECLARE_NULL_NAMESPACE_BYTES_TEMPLATE1 (OT, RangeRecord, 9); +// but that only works when there is only a single namespace level. +// The macro should probably be fixed so it can work in this situation. +extern HB_INTERNAL const unsigned char _hb_Null_OT_RangeRecord[9]; +template +struct Null> { + static OT::Layout::Common::RangeRecord const & get_null () { + return *reinterpret_cast *> (_hb_Null_OT_RangeRecord); + } +}; + + +#endif // #ifndef OT_LAYOUT_COMMON_RANGERECORD_HH diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GDEF/GDEF.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GDEF/GDEF.hh new file mode 100644 index 00000000000..dd025c1284d --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GDEF/GDEF.hh @@ -0,0 +1,1022 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2010,2011,2012 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef OT_LAYOUT_GDEF_GDEF_HH +#define OT_LAYOUT_GDEF_GDEF_HH + +#include "../../../hb-ot-var-common.hh" + +#include "../../../hb-font.hh" +#include "../../../hb-cache.hh" + + +namespace OT { + + +/* + * Attachment List Table + */ + +/* Array of contour point indices--in increasing numerical order */ +struct AttachPoint : Array16Of +{ + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + return_trace (out->serialize (c->serializer, + iter ())); + } +}; + +struct AttachList +{ + unsigned int get_attach_points (hb_codepoint_t glyph_id, + unsigned int start_offset, + unsigned int *point_count /* IN/OUT */, + unsigned int *point_array /* OUT */) const + { + unsigned int index = (this+coverage).get_coverage (glyph_id); + if (index == NOT_COVERED) + { + if (point_count) + *point_count = 0; + return 0; + } + + const AttachPoint &points = this+attachPoint[index]; + + if (point_count) + { + + points.as_array ().sub_array (start_offset, point_count) + | hb_sink (hb_array (point_array, *point_count)) + ; + } + + return points.len; + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + hb_sorted_vector_t new_coverage; + + hb_zip (this+coverage, attachPoint) + | hb_filter (glyphset, hb_first) + | hb_filter (subset_offset_array (c, out->attachPoint, this), hb_second) + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + out->coverage.serialize_serialize (c->serializer, new_coverage.iter ()); + return_trace (bool (new_coverage)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (coverage.sanitize (c, this) && attachPoint.sanitize (c, this)); + } + + protected: + Offset16To + coverage; /* Offset to Coverage table -- from + * beginning of AttachList table */ + Array16OfOffset16To + attachPoint; /* Array of AttachPoint tables + * in Coverage Index order */ + public: + DEFINE_SIZE_ARRAY (4, attachPoint); +}; + +/* + * Ligature Caret Table + */ + +struct CaretValueFormat1 +{ + friend struct CaretValue; + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + return_trace (true); + } + + private: + hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction) const + { + return HB_DIRECTION_IS_HORIZONTAL (direction) ? font->em_scale_x (coordinate) : font->em_scale_y (coordinate); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + HBUINT16 caretValueFormat; /* Format identifier--format = 1 */ + FWORD coordinate; /* X or Y value, in design units */ + public: + DEFINE_SIZE_STATIC (4); +}; + +struct CaretValueFormat2 +{ + friend struct CaretValue; + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + return_trace (true); + } + + private: + hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction, hb_codepoint_t glyph_id) const + { + hb_position_t x, y; + font->get_glyph_contour_point_for_origin (glyph_id, caretValuePoint, direction, &x, &y); + return HB_DIRECTION_IS_HORIZONTAL (direction) ? x : y; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + HBUINT16 caretValueFormat; /* Format identifier--format = 2 */ + HBUINT16 caretValuePoint; /* Contour point index on glyph */ + public: + DEFINE_SIZE_STATIC (4); +}; + +struct CaretValueFormat3 +{ + friend struct CaretValue; + + hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction, + const VariationStore &var_store) const + { + return HB_DIRECTION_IS_HORIZONTAL (direction) ? + font->em_scale_x (coordinate) + (this+deviceTable).get_x_delta (font, var_store) : + font->em_scale_y (coordinate) + (this+deviceTable).get_y_delta (font, var_store); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (!c->serializer->embed (caretValueFormat)) return_trace (false); + if (!c->serializer->embed (coordinate)) return_trace (false); + + unsigned varidx = (this+deviceTable).get_variation_index (); + hb_pair_t *new_varidx_delta; + if (!c->plan->layout_variation_idx_delta_map.has (varidx, &new_varidx_delta)) + return_trace (false); + + uint32_t new_varidx = hb_first (*new_varidx_delta); + int delta = hb_second (*new_varidx_delta); + if (delta != 0) + { + if (!c->serializer->check_assign (out->coordinate, coordinate + delta, HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); + } + + if (new_varidx == HB_OT_LAYOUT_NO_VARIATIONS_INDEX) + return_trace (c->serializer->check_assign (out->caretValueFormat, 1, HB_SERIALIZE_ERROR_INT_OVERFLOW)); + + if (!c->serializer->embed (deviceTable)) + return_trace (false); + + return_trace (out->deviceTable.serialize_copy (c->serializer, deviceTable, this, c->serializer->to_bias (out), + hb_serialize_context_t::Head, &c->plan->layout_variation_idx_delta_map)); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { (this+deviceTable).collect_variation_indices (c); } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && deviceTable.sanitize (c, this)); + } + + protected: + HBUINT16 caretValueFormat; /* Format identifier--format = 3 */ + FWORD coordinate; /* X or Y value, in design units */ + Offset16To + deviceTable; /* Offset to Device table for X or Y + * value--from beginning of CaretValue + * table */ + public: + DEFINE_SIZE_STATIC (6); +}; + +struct CaretValue +{ + hb_position_t get_caret_value (hb_font_t *font, + hb_direction_t direction, + hb_codepoint_t glyph_id, + const VariationStore &var_store) const + { + switch (u.format) { + case 1: return u.format1.get_caret_value (font, direction); + case 2: return u.format2.get_caret_value (font, direction, glyph_id); + case 3: return u.format3.get_caret_value (font, direction, var_store); + default:return 0; + } + } + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); + case 3: return_trace (c->dispatch (u.format3, std::forward (ds)...)); + default:return_trace (c->default_return_value ()); + } + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + switch (u.format) { + case 1: + case 2: + return; + case 3: + u.format3.collect_variation_indices (c); + return; + default: return; + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.format.sanitize (c)) return_trace (false); + switch (u.format) { + case 1: return_trace (u.format1.sanitize (c)); + case 2: return_trace (u.format2.sanitize (c)); + case 3: return_trace (u.format3.sanitize (c)); + default:return_trace (true); + } + } + + protected: + union { + HBUINT16 format; /* Format identifier */ + CaretValueFormat1 format1; + CaretValueFormat2 format2; + CaretValueFormat3 format3; + } u; + public: + DEFINE_SIZE_UNION (2, format); +}; + +struct LigGlyph +{ + unsigned get_lig_carets (hb_font_t *font, + hb_direction_t direction, + hb_codepoint_t glyph_id, + const VariationStore &var_store, + unsigned start_offset, + unsigned *caret_count /* IN/OUT */, + hb_position_t *caret_array /* OUT */) const + { + if (caret_count) + { + + carets.as_array ().sub_array (start_offset, caret_count) + | hb_map (hb_add (this)) + | hb_map ([&] (const CaretValue &value) { return value.get_caret_value (font, direction, glyph_id, var_store); }) + | hb_sink (hb_array (caret_array, *caret_count)) + ; + } + + return carets.len; + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + + hb_iter (carets) + | hb_apply (subset_offset_array (c, out->carets, this)) + ; + + return_trace (bool (out->carets)); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + for (const Offset16To& offset : carets.iter ()) + (this+offset).collect_variation_indices (c); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (carets.sanitize (c, this)); + } + + protected: + Array16OfOffset16To + carets; /* Offset array of CaretValue tables + * --from beginning of LigGlyph table + * --in increasing coordinate order */ + public: + DEFINE_SIZE_ARRAY (2, carets); +}; + +struct LigCaretList +{ + unsigned int get_lig_carets (hb_font_t *font, + hb_direction_t direction, + hb_codepoint_t glyph_id, + const VariationStore &var_store, + unsigned int start_offset, + unsigned int *caret_count /* IN/OUT */, + hb_position_t *caret_array /* OUT */) const + { + unsigned int index = (this+coverage).get_coverage (glyph_id); + if (index == NOT_COVERED) + { + if (caret_count) + *caret_count = 0; + return 0; + } + const LigGlyph &lig_glyph = this+ligGlyph[index]; + return lig_glyph.get_lig_carets (font, direction, glyph_id, var_store, start_offset, caret_count, caret_array); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + hb_sorted_vector_t new_coverage; + + hb_zip (this+coverage, ligGlyph) + | hb_filter (glyphset, hb_first) + | hb_filter (subset_offset_array (c, out->ligGlyph, this), hb_second) + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + out->coverage.serialize_serialize (c->serializer, new_coverage.iter ()); + return_trace (bool (new_coverage)); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + + hb_zip (this+coverage, ligGlyph) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([c] (const LigGlyph& _) { _.collect_variation_indices (c); }) + ; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (coverage.sanitize (c, this) && ligGlyph.sanitize (c, this)); + } + + protected: + Offset16To + coverage; /* Offset to Coverage table--from + * beginning of LigCaretList table */ + Array16OfOffset16To + ligGlyph; /* Array of LigGlyph tables + * in Coverage Index order */ + public: + DEFINE_SIZE_ARRAY (4, ligGlyph); +}; + + +struct MarkGlyphSetsFormat1 +{ + bool covers (unsigned int set_index, hb_codepoint_t glyph_id) const + { return (this+coverage[set_index]).get_coverage (glyph_id) != NOT_COVERED; } + + template + void collect_coverage (hb_vector_t &sets) const + { + for (const auto &offset : coverage) + { + const auto &cov = this+offset; + cov.collect_coverage (sets.push ()); + } + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + bool ret = true; + for (const Offset32To& offset : coverage.iter ()) + { + auto *o = out->coverage.serialize_append (c->serializer); + if (unlikely (!o)) + { + ret = false; + break; + } + + //not using o->serialize_subset (c, offset, this, out) here because + //OTS doesn't allow null offset. + //See issue: https://github.com/khaledhosny/ots/issues/172 + c->serializer->push (); + c->dispatch (this+offset); + c->serializer->add_link (*o, c->serializer->pop_pack ()); + } + + return_trace (ret && out->coverage.len); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (coverage.sanitize (c, this)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + Array16Of> + coverage; /* Array of long offsets to mark set + * coverage tables */ + public: + DEFINE_SIZE_ARRAY (4, coverage); +}; + +struct MarkGlyphSets +{ + bool covers (unsigned int set_index, hb_codepoint_t glyph_id) const + { + switch (u.format) { + case 1: return u.format1.covers (set_index, glyph_id); + default:return false; + } + } + + template + void collect_coverage (hb_vector_t &sets) const + { + switch (u.format) { + case 1: u.format1.collect_coverage (sets); return; + default:return; + } + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + switch (u.format) { + case 1: return_trace (u.format1.subset (c)); + default:return_trace (false); + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.format.sanitize (c)) return_trace (false); + switch (u.format) { + case 1: return_trace (u.format1.sanitize (c)); + default:return_trace (true); + } + } + + protected: + union { + HBUINT16 format; /* Format identifier */ + MarkGlyphSetsFormat1 format1; + } u; + public: + DEFINE_SIZE_UNION (2, format); +}; + + +/* + * GDEF -- Glyph Definition + * https://docs.microsoft.com/en-us/typography/opentype/spec/gdef + */ + + +template +struct GDEFVersion1_2 +{ + friend struct GDEF; + + protected: + FixedVersion<>version; /* Version of the GDEF table--currently + * 0x00010003u */ + typename Types::template OffsetTo + glyphClassDef; /* Offset to class definition table + * for glyph type--from beginning of + * GDEF header (may be Null) */ + typename Types::template OffsetTo + attachList; /* Offset to list of glyphs with + * attachment points--from beginning + * of GDEF header (may be Null) */ + typename Types::template OffsetTo + ligCaretList; /* Offset to list of positioning points + * for ligature carets--from beginning + * of GDEF header (may be Null) */ + typename Types::template OffsetTo + markAttachClassDef; /* Offset to class definition table for + * mark attachment type--from beginning + * of GDEF header (may be Null) */ + typename Types::template OffsetTo + markGlyphSetsDef; /* Offset to the table of mark set + * definitions--from beginning of GDEF + * header (may be NULL). Introduced + * in version 0x00010002. */ + Offset32To + varStore; /* Offset to the table of Item Variation + * Store--from beginning of GDEF + * header (may be NULL). Introduced + * in version 0x00010003. */ + public: + DEFINE_SIZE_MIN (4 + 4 * Types::size); + + unsigned int get_size () const + { + return min_size + + (version.to_int () >= 0x00010002u ? markGlyphSetsDef.static_size : 0) + + (version.to_int () >= 0x00010003u ? varStore.static_size : 0); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (version.sanitize (c) && + glyphClassDef.sanitize (c, this) && + attachList.sanitize (c, this) && + ligCaretList.sanitize (c, this) && + markAttachClassDef.sanitize (c, this) && + (version.to_int () < 0x00010002u || markGlyphSetsDef.sanitize (c, this)) && + (version.to_int () < 0x00010003u || varStore.sanitize (c, this))); + } + + static void remap_varidx_after_instantiation (const hb_map_t& varidx_map, + hb_hashmap_t>& layout_variation_idx_delta_map /* IN/OUT */) + { + /* varidx_map is empty which means varstore is empty after instantiation, + * no variations, map all varidx to HB_OT_LAYOUT_NO_VARIATIONS_INDEX. + * varidx_map doesn't have original varidx, indicating delta row is all + * zeros, map varidx to HB_OT_LAYOUT_NO_VARIATIONS_INDEX */ + for (auto _ : layout_variation_idx_delta_map.iter_ref ()) + { + /* old_varidx->(varidx, delta) mapping generated for subsetting, then this + * varidx is used as key of varidx_map during instantiation */ + uint32_t varidx = _.second.first; + uint32_t *new_varidx; + if (varidx_map.has (varidx, &new_varidx)) + _.second.first = *new_varidx; + else + _.second.first = HB_OT_LAYOUT_NO_VARIATIONS_INDEX; + } + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (*this); + if (unlikely (!out)) return_trace (false); + + bool subset_glyphclassdef = out->glyphClassDef.serialize_subset (c, glyphClassDef, this, nullptr, false, true); + bool subset_attachlist = out->attachList.serialize_subset (c, attachList, this); + bool subset_ligcaretlist = out->ligCaretList.serialize_subset (c, ligCaretList, this); + bool subset_markattachclassdef = out->markAttachClassDef.serialize_subset (c, markAttachClassDef, this, nullptr, false, true); + + bool subset_markglyphsetsdef = false; + if (version.to_int () >= 0x00010002u) + { + subset_markglyphsetsdef = out->markGlyphSetsDef.serialize_subset (c, markGlyphSetsDef, this); + } + + bool subset_varstore = false; + if (version.to_int () >= 0x00010003u) + { + if (c->plan->all_axes_pinned) + out->varStore = 0; + else if (c->plan->normalized_coords) + { + if (varStore) + { + item_variations_t item_vars; + if (item_vars.instantiate (this+varStore, c->plan, true, true, + c->plan->gdef_varstore_inner_maps.as_array ())) + subset_varstore = out->varStore.serialize_serialize (c->serializer, + item_vars.has_long_word (), + c->plan->axis_tags, + item_vars.get_region_list (), + item_vars.get_vardata_encodings ()); + remap_varidx_after_instantiation (item_vars.get_varidx_map (), + c->plan->layout_variation_idx_delta_map); + } + } + else + subset_varstore = out->varStore.serialize_subset (c, varStore, this, c->plan->gdef_varstore_inner_maps.as_array ()); + } + + if (subset_varstore) + { + out->version.minor = 3; + } else if (subset_markglyphsetsdef) { + out->version.minor = 2; + } else { + out->version.minor = 0; + } + + return_trace (subset_glyphclassdef || subset_attachlist || + subset_ligcaretlist || subset_markattachclassdef || + (out->version.to_int () >= 0x00010002u && subset_markglyphsetsdef) || + (out->version.to_int () >= 0x00010003u && subset_varstore)); + } +}; + +struct GDEF +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_GDEF; + + enum GlyphClasses { + UnclassifiedGlyph = 0, + BaseGlyph = 1, + LigatureGlyph = 2, + MarkGlyph = 3, + ComponentGlyph = 4 + }; + + unsigned int get_size () const + { + switch (u.version.major) { + case 1: return u.version1.get_size (); +#ifndef HB_NO_BEYOND_64K + case 2: return u.version2.get_size (); +#endif + default: return u.version.static_size; + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!u.version.sanitize (c))) return_trace (false); + switch (u.version.major) { + case 1: return_trace (u.version1.sanitize (c)); +#ifndef HB_NO_BEYOND_64K + case 2: return_trace (u.version2.sanitize (c)); +#endif + default: return_trace (true); + } + } + + bool subset (hb_subset_context_t *c) const + { + switch (u.version.major) { + case 1: return u.version1.subset (c); +#ifndef HB_NO_BEYOND_64K + case 2: return u.version2.subset (c); +#endif + default: return false; + } + } + + bool has_glyph_classes () const + { + switch (u.version.major) { + case 1: return u.version1.glyphClassDef != 0; +#ifndef HB_NO_BEYOND_64K + case 2: return u.version2.glyphClassDef != 0; +#endif + default: return false; + } + } + const ClassDef &get_glyph_class_def () const + { + switch (u.version.major) { + case 1: return this+u.version1.glyphClassDef; +#ifndef HB_NO_BEYOND_64K + case 2: return this+u.version2.glyphClassDef; +#endif + default: return Null(ClassDef); + } + } + bool has_attach_list () const + { + switch (u.version.major) { + case 1: return u.version1.attachList != 0; +#ifndef HB_NO_BEYOND_64K + case 2: return u.version2.attachList != 0; +#endif + default: return false; + } + } + const AttachList &get_attach_list () const + { + switch (u.version.major) { + case 1: return this+u.version1.attachList; +#ifndef HB_NO_BEYOND_64K + case 2: return this+u.version2.attachList; +#endif + default: return Null(AttachList); + } + } + bool has_lig_carets () const + { + switch (u.version.major) { + case 1: return u.version1.ligCaretList != 0; +#ifndef HB_NO_BEYOND_64K + case 2: return u.version2.ligCaretList != 0; +#endif + default: return false; + } + } + const LigCaretList &get_lig_caret_list () const + { + switch (u.version.major) { + case 1: return this+u.version1.ligCaretList; +#ifndef HB_NO_BEYOND_64K + case 2: return this+u.version2.ligCaretList; +#endif + default: return Null(LigCaretList); + } + } + bool has_mark_attachment_types () const + { + switch (u.version.major) { + case 1: return u.version1.markAttachClassDef != 0; +#ifndef HB_NO_BEYOND_64K + case 2: return u.version2.markAttachClassDef != 0; +#endif + default: return false; + } + } + const ClassDef &get_mark_attach_class_def () const + { + switch (u.version.major) { + case 1: return this+u.version1.markAttachClassDef; +#ifndef HB_NO_BEYOND_64K + case 2: return this+u.version2.markAttachClassDef; +#endif + default: return Null(ClassDef); + } + } + bool has_mark_glyph_sets () const + { + switch (u.version.major) { + case 1: return u.version.to_int () >= 0x00010002u && u.version1.markGlyphSetsDef != 0; +#ifndef HB_NO_BEYOND_64K + case 2: return u.version2.markGlyphSetsDef != 0; +#endif + default: return false; + } + } + const MarkGlyphSets &get_mark_glyph_sets () const + { + switch (u.version.major) { + case 1: return u.version.to_int () >= 0x00010002u ? this+u.version1.markGlyphSetsDef : Null(MarkGlyphSets); +#ifndef HB_NO_BEYOND_64K + case 2: return this+u.version2.markGlyphSetsDef; +#endif + default: return Null(MarkGlyphSets); + } + } + bool has_var_store () const + { + switch (u.version.major) { + case 1: return u.version.to_int () >= 0x00010003u && u.version1.varStore != 0; +#ifndef HB_NO_BEYOND_64K + case 2: return u.version2.varStore != 0; +#endif + default: return false; + } + } + const VariationStore &get_var_store () const + { + switch (u.version.major) { + case 1: return u.version.to_int () >= 0x00010003u ? this+u.version1.varStore : Null(VariationStore); +#ifndef HB_NO_BEYOND_64K + case 2: return this+u.version2.varStore; +#endif + default: return Null(VariationStore); + } + } + + + bool has_data () const { return u.version.to_int (); } + unsigned int get_glyph_class (hb_codepoint_t glyph) const + { return get_glyph_class_def ().get_class (glyph); } + void get_glyphs_in_class (unsigned int klass, hb_set_t *glyphs) const + { get_glyph_class_def ().collect_class (glyphs, klass); } + + unsigned int get_mark_attachment_type (hb_codepoint_t glyph) const + { return get_mark_attach_class_def ().get_class (glyph); } + + unsigned int get_attach_points (hb_codepoint_t glyph_id, + unsigned int start_offset, + unsigned int *point_count /* IN/OUT */, + unsigned int *point_array /* OUT */) const + { return get_attach_list ().get_attach_points (glyph_id, start_offset, point_count, point_array); } + + unsigned int get_lig_carets (hb_font_t *font, + hb_direction_t direction, + hb_codepoint_t glyph_id, + unsigned int start_offset, + unsigned int *caret_count /* IN/OUT */, + hb_position_t *caret_array /* OUT */) const + { return get_lig_caret_list ().get_lig_carets (font, + direction, glyph_id, get_var_store(), + start_offset, caret_count, caret_array); } + + bool mark_set_covers (unsigned int set_index, hb_codepoint_t glyph_id) const + { return get_mark_glyph_sets ().covers (set_index, glyph_id); } + + /* glyph_props is a 16-bit integer where the lower 8-bit have bits representing + * glyph class and other bits, and high 8-bit the mark attachment type (if any). + * Not to be confused with lookup_props which is very similar. */ + unsigned int get_glyph_props (hb_codepoint_t glyph) const + { + unsigned int klass = get_glyph_class (glyph); + + static_assert (((unsigned int) HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH == (unsigned int) LookupFlag::IgnoreBaseGlyphs), ""); + static_assert (((unsigned int) HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE == (unsigned int) LookupFlag::IgnoreLigatures), ""); + static_assert (((unsigned int) HB_OT_LAYOUT_GLYPH_PROPS_MARK == (unsigned int) LookupFlag::IgnoreMarks), ""); + + switch (klass) { + default: return HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED; + case BaseGlyph: return HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH; + case LigatureGlyph: return HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE; + case MarkGlyph: + klass = get_mark_attachment_type (glyph); + return HB_OT_LAYOUT_GLYPH_PROPS_MARK | (klass << 8); + } + } + + HB_INTERNAL bool is_blocklisted (hb_blob_t *blob, + hb_face_t *face) const; + + struct accelerator_t + { + accelerator_t (hb_face_t *face) + { + table = hb_sanitize_context_t ().reference_table (face); + if (unlikely (table->is_blocklisted (table.get_blob (), face))) + { + hb_blob_destroy (table.get_blob ()); + table = hb_blob_get_empty (); + } + +#ifndef HB_NO_GDEF_CACHE + table->get_mark_glyph_sets ().collect_coverage (mark_glyph_set_digests); +#endif + } + ~accelerator_t () { table.destroy (); } + + unsigned int get_glyph_props (hb_codepoint_t glyph) const + { + unsigned v; + +#ifndef HB_NO_GDEF_CACHE + if (glyph_props_cache.get (glyph, &v)) + return v; +#endif + + v = table->get_glyph_props (glyph); + +#ifndef HB_NO_GDEF_CACHE + if (likely (table.get_blob ())) // Don't try setting if we are the null instance! + glyph_props_cache.set (glyph, v); +#endif + + return v; + + } + + bool mark_set_covers (unsigned int set_index, hb_codepoint_t glyph_id) const + { + return +#ifndef HB_NO_GDEF_CACHE + mark_glyph_set_digests[set_index].may_have (glyph_id) && +#endif + table->mark_set_covers (set_index, glyph_id); + } + + hb_blob_ptr_t table; +#ifndef HB_NO_GDEF_CACHE + hb_vector_t mark_glyph_set_digests; + mutable hb_cache_t<21, 3, 8> glyph_props_cache; +#endif + }; + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { get_lig_caret_list ().collect_variation_indices (c); } + + void remap_layout_variation_indices (const hb_set_t *layout_variation_indices, + const hb_vector_t& normalized_coords, + bool calculate_delta, /* not pinned at default */ + bool no_variations, /* all axes pinned */ + hb_hashmap_t> *layout_variation_idx_delta_map /* OUT */) const + { + if (!has_var_store ()) return; + const VariationStore &var_store = get_var_store (); + float *store_cache = var_store.create_cache (); + + unsigned new_major = 0, new_minor = 0; + unsigned last_major = (layout_variation_indices->get_min ()) >> 16; + for (unsigned idx : layout_variation_indices->iter ()) + { + int delta = 0; + if (calculate_delta) + delta = roundf (var_store.get_delta (idx, normalized_coords.arrayZ, + normalized_coords.length, store_cache)); + + if (no_variations) + { + layout_variation_idx_delta_map->set (idx, hb_pair_t (HB_OT_LAYOUT_NO_VARIATIONS_INDEX, delta)); + continue; + } + + uint16_t major = idx >> 16; + if (major >= var_store.get_sub_table_count ()) break; + if (major != last_major) + { + new_minor = 0; + ++new_major; + } + + unsigned new_idx = (new_major << 16) + new_minor; + layout_variation_idx_delta_map->set (idx, hb_pair_t (new_idx, delta)); + ++new_minor; + last_major = major; + } + var_store.destroy_cache (store_cache); + } + + protected: + union { + FixedVersion<> version; /* Version identifier */ + GDEFVersion1_2 version1; +#ifndef HB_NO_BEYOND_64K + GDEFVersion1_2 version2; +#endif + } u; + public: + DEFINE_SIZE_MIN (4); +}; + +struct GDEF_accelerator_t : GDEF::accelerator_t { + GDEF_accelerator_t (hb_face_t *face) : GDEF::accelerator_t (face) {} +}; + +} /* namespace OT */ + + +#endif /* OT_LAYOUT_GDEF_GDEF_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/Anchor.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/Anchor.hh new file mode 100644 index 00000000000..49e76e77509 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/Anchor.hh @@ -0,0 +1,83 @@ +#ifndef OT_LAYOUT_GPOS_ANCHOR_HH +#define OT_LAYOUT_GPOS_ANCHOR_HH + +#include "AnchorFormat1.hh" +#include "AnchorFormat2.hh" +#include "AnchorFormat3.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct Anchor +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + AnchorFormat1 format1; + AnchorFormat2 format2; + AnchorFormat3 format3; + } u; + public: + DEFINE_SIZE_UNION (2, format); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.format.sanitize (c)) return_trace (false); + switch (u.format) { + case 1: return_trace (u.format1.sanitize (c)); + case 2: return_trace (u.format2.sanitize (c)); + case 3: return_trace (u.format3.sanitize (c)); + default:return_trace (true); + } + } + + void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id, + float *x, float *y) const + { + *x = *y = 0; + switch (u.format) { + case 1: u.format1.get_anchor (c, glyph_id, x, y); return; + case 2: u.format2.get_anchor (c, glyph_id, x, y); return; + case 3: u.format3.get_anchor (c, glyph_id, x, y); return; + default: return; + } + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + switch (u.format) { + case 1: return_trace (bool (reinterpret_cast (u.format1.copy (c->serializer)))); + case 2: + if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING) + { + // AnchorFormat 2 just containins extra hinting information, so + // if hints are being dropped convert to format 1. + return_trace (bool (reinterpret_cast (u.format1.copy (c->serializer)))); + } + return_trace (bool (reinterpret_cast (u.format2.copy (c->serializer)))); + case 3: return_trace (u.format3.subset (c)); + default:return_trace (false); + } + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + switch (u.format) { + case 1: case 2: + return; + case 3: + u.format3.collect_variation_indices (c); + return; + default: return; + } + } +}; + +} +} +} + +#endif // OT_LAYOUT_GPOS_ANCHOR_HH diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/AnchorFormat1.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/AnchorFormat1.hh new file mode 100644 index 00000000000..738cc31bbfa --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/AnchorFormat1.hh @@ -0,0 +1,46 @@ +#ifndef OT_LAYOUT_GPOS_ANCHORFORMAT1_HH +#define OT_LAYOUT_GPOS_ANCHORFORMAT1_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct AnchorFormat1 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + FWORD xCoordinate; /* Horizontal value--in design units */ + FWORD yCoordinate; /* Vertical value--in design units */ + public: + DEFINE_SIZE_STATIC (6); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id HB_UNUSED, + float *x, float *y) const + { + hb_font_t *font = c->font; + *x = font->em_fscale_x (xCoordinate); + *y = font->em_fscale_y (yCoordinate); + } + + AnchorFormat1* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + AnchorFormat1* out = c->embed (this); + if (!out) return_trace (out); + out->format = 1; + return_trace (out); + } +}; + + +} +} +} + +#endif // OT_LAYOUT_GPOS_ANCHORFORMAT1_HH diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/AnchorFormat2.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/AnchorFormat2.hh new file mode 100644 index 00000000000..70b4d19f53c --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/AnchorFormat2.hh @@ -0,0 +1,58 @@ +#ifndef OT_LAYOUT_GPOS_ANCHORFORMAT2_HH +#define OT_LAYOUT_GPOS_ANCHORFORMAT2_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct AnchorFormat2 +{ + + protected: + HBUINT16 format; /* Format identifier--format = 2 */ + FWORD xCoordinate; /* Horizontal value--in design units */ + FWORD yCoordinate; /* Vertical value--in design units */ + HBUINT16 anchorPoint; /* Index to glyph contour point */ + public: + DEFINE_SIZE_STATIC (8); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id, + float *x, float *y) const + { + hb_font_t *font = c->font; + +#ifdef HB_NO_HINTING + *x = font->em_fscale_x (xCoordinate); + *y = font->em_fscale_y (yCoordinate); + return; +#endif + + unsigned int x_ppem = font->x_ppem; + unsigned int y_ppem = font->y_ppem; + hb_position_t cx = 0, cy = 0; + bool ret; + + ret = (x_ppem || y_ppem) && + font->get_glyph_contour_point_for_origin (glyph_id, anchorPoint, HB_DIRECTION_LTR, &cx, &cy); + *x = ret && x_ppem ? cx : font->em_fscale_x (xCoordinate); + *y = ret && y_ppem ? cy : font->em_fscale_y (yCoordinate); + } + + AnchorFormat2* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + return_trace (c->embed (this)); + } +}; + +} +} +} + +#endif // OT_LAYOUT_GPOS_ANCHORFORMAT2_HH diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/AnchorFormat3.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/AnchorFormat3.hh new file mode 100644 index 00000000000..56eda4a5778 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/AnchorFormat3.hh @@ -0,0 +1,114 @@ +#ifndef OT_LAYOUT_GPOS_ANCHORFORMAT3_HH +#define OT_LAYOUT_GPOS_ANCHORFORMAT3_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct AnchorFormat3 +{ + protected: + HBUINT16 format; /* Format identifier--format = 3 */ + FWORD xCoordinate; /* Horizontal value--in design units */ + FWORD yCoordinate; /* Vertical value--in design units */ + Offset16To + xDeviceTable; /* Offset to Device table for X + * coordinate-- from beginning of + * Anchor table (may be NULL) */ + Offset16To + yDeviceTable; /* Offset to Device table for Y + * coordinate-- from beginning of + * Anchor table (may be NULL) */ + public: + DEFINE_SIZE_STATIC (10); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) return_trace (false); + + return_trace (xDeviceTable.sanitize (c, this) && yDeviceTable.sanitize (c, this)); + } + + void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id HB_UNUSED, + float *x, float *y) const + { + hb_font_t *font = c->font; + *x = font->em_fscale_x (xCoordinate); + *y = font->em_fscale_y (yCoordinate); + + if ((font->x_ppem || font->num_coords) && xDeviceTable.sanitize (&c->sanitizer, this)) + *x += (this+xDeviceTable).get_x_delta (font, c->var_store, c->var_store_cache); + if ((font->y_ppem || font->num_coords) && yDeviceTable.sanitize (&c->sanitizer, this)) + *y += (this+yDeviceTable).get_y_delta (font, c->var_store, c->var_store_cache); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->embed (format))) return_trace (false); + if (unlikely (!c->serializer->embed (xCoordinate))) return_trace (false); + if (unlikely (!c->serializer->embed (yCoordinate))) return_trace (false); + + unsigned x_varidx = xDeviceTable ? (this+xDeviceTable).get_variation_index () : HB_OT_LAYOUT_NO_VARIATIONS_INDEX; + if (x_varidx != HB_OT_LAYOUT_NO_VARIATIONS_INDEX) + { + hb_pair_t *new_varidx_delta; + if (!c->plan->layout_variation_idx_delta_map.has (x_varidx, &new_varidx_delta)) + return_trace (false); + + x_varidx = hb_first (*new_varidx_delta); + int delta = hb_second (*new_varidx_delta); + if (delta != 0) + { + if (!c->serializer->check_assign (out->xCoordinate, xCoordinate + delta, + HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); + } + } + + unsigned y_varidx = yDeviceTable ? (this+yDeviceTable).get_variation_index () : HB_OT_LAYOUT_NO_VARIATIONS_INDEX; + if (y_varidx != HB_OT_LAYOUT_NO_VARIATIONS_INDEX) + { + hb_pair_t *new_varidx_delta; + if (!c->plan->layout_variation_idx_delta_map.has (y_varidx, &new_varidx_delta)) + return_trace (false); + + y_varidx = hb_first (*new_varidx_delta); + int delta = hb_second (*new_varidx_delta); + if (delta != 0) + { + if (!c->serializer->check_assign (out->yCoordinate, yCoordinate + delta, + HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); + } + } + + /* in case that all axes are pinned or no variations after instantiation, + * both var_idxes will be mapped to HB_OT_LAYOUT_NO_VARIATIONS_INDEX */ + if (x_varidx == HB_OT_LAYOUT_NO_VARIATIONS_INDEX && + y_varidx == HB_OT_LAYOUT_NO_VARIATIONS_INDEX) + return_trace (c->serializer->check_assign (out->format, 1, HB_SERIALIZE_ERROR_INT_OVERFLOW)); + + if (!c->serializer->embed (xDeviceTable)) return_trace (false); + if (!c->serializer->embed (yDeviceTable)) return_trace (false); + + out->xDeviceTable.serialize_copy (c->serializer, xDeviceTable, this, 0, hb_serialize_context_t::Head, &c->plan->layout_variation_idx_delta_map); + out->yDeviceTable.serialize_copy (c->serializer, yDeviceTable, this, 0, hb_serialize_context_t::Head, &c->plan->layout_variation_idx_delta_map); + return_trace (out); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + (this+xDeviceTable).collect_variation_indices (c); + (this+yDeviceTable).collect_variation_indices (c); + } +}; + + +} +} +} + +#endif // OT_LAYOUT_GPOS_ANCHORFORMAT3_HH diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/AnchorMatrix.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/AnchorMatrix.hh new file mode 100644 index 00000000000..37ba7916f2a --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/AnchorMatrix.hh @@ -0,0 +1,85 @@ +#ifndef OT_LAYOUT_GPOS_ANCHORMATRIX_HH +#define OT_LAYOUT_GPOS_ANCHORMATRIX_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct AnchorMatrix +{ + HBUINT16 rows; /* Number of rows */ + UnsizedArrayOf> + matrixZ; /* Matrix of offsets to Anchor tables-- + * from beginning of AnchorMatrix table */ + public: + DEFINE_SIZE_ARRAY (2, matrixZ); + + bool sanitize (hb_sanitize_context_t *c, unsigned int cols) const + { + TRACE_SANITIZE (this); + if (!c->check_struct (this)) return_trace (false); + if (unlikely (hb_unsigned_mul_overflows (rows, cols))) return_trace (false); + unsigned int count = rows * cols; + if (!c->check_array (matrixZ.arrayZ, count)) return_trace (false); + + if (c->lazy_some_gpos) + return_trace (true); + + for (unsigned int i = 0; i < count; i++) + if (!matrixZ[i].sanitize (c, this)) return_trace (false); + return_trace (true); + } + + const Anchor& get_anchor (hb_ot_apply_context_t *c, + unsigned int row, unsigned int col, + unsigned int cols, bool *found) const + { + *found = false; + if (unlikely (row >= rows || col >= cols)) return Null (Anchor); + auto &offset = matrixZ[row * cols + col]; + if (unlikely (!offset.sanitize (&c->sanitizer, this))) return Null (Anchor); + *found = !offset.is_null (); + return this+offset; + } + + template + void collect_variation_indices (hb_collect_variation_indices_context_t *c, + Iterator index_iter) const + { + for (unsigned i : index_iter) + (this+matrixZ[i]).collect_variation_indices (c); + } + + template + bool subset (hb_subset_context_t *c, + unsigned num_rows, + Iterator index_iter) const + { + TRACE_SUBSET (this); + + auto *out = c->serializer->start_embed (this); + + if (!index_iter) return_trace (false); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + out->rows = num_rows; + bool ret = false; + for (const unsigned i : index_iter) + { + auto *offset = c->serializer->embed (matrixZ[i]); + if (!offset) return_trace (false); + ret |= offset->serialize_subset (c, matrixZ[i], this); + } + + return_trace (ret); + } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GPOS_ANCHORMATRIX_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/ChainContextPos.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/ChainContextPos.hh new file mode 100644 index 00000000000..d551ac2a2be --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/ChainContextPos.hh @@ -0,0 +1,14 @@ +#ifndef OT_LAYOUT_GPOS_CHAINCONTEXTPOS_HH +#define OT_LAYOUT_GPOS_CHAINCONTEXTPOS_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct ChainContextPos : ChainContext {}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_CHAINCONTEXTPOS_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/Common.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/Common.hh new file mode 100644 index 00000000000..408197454f1 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/Common.hh @@ -0,0 +1,33 @@ +#ifndef OT_LAYOUT_GPOS_COMMON_HH +#define OT_LAYOUT_GPOS_COMMON_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +enum attach_type_t { + ATTACH_TYPE_NONE = 0X00, + + /* Each attachment should be either a mark or a cursive; can't be both. */ + ATTACH_TYPE_MARK = 0X01, + ATTACH_TYPE_CURSIVE = 0X02, +}; + +/* buffer **position** var allocations */ +#define attach_chain() var.i16[0] /* glyph to which this attaches to, relative to current glyphs; negative for going back, positive for forward. */ +#define attach_type() var.u8[2] /* attachment type */ +/* Note! if attach_chain() is zero, the value of attach_type() is irrelevant. */ + +template +static void SinglePos_serialize (hb_serialize_context_t *c, + const SrcLookup *src, + Iterator it, + const hb_hashmap_t> *layout_variation_idx_delta_map, + bool all_axes_pinned); + + +} +} +} + +#endif // OT_LAYOUT_GPOS_COMMON_HH diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/ContextPos.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/ContextPos.hh new file mode 100644 index 00000000000..2a01eaa3a68 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/ContextPos.hh @@ -0,0 +1,14 @@ +#ifndef OT_LAYOUT_GPOS_CONTEXTPOS_HH +#define OT_LAYOUT_GPOS_CONTEXTPOS_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct ContextPos : Context {}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_CONTEXTPOS_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/CursivePos.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/CursivePos.hh new file mode 100644 index 00000000000..0105a9b8542 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/CursivePos.hh @@ -0,0 +1,35 @@ +#ifndef OT_LAYOUT_GPOS_CURSIVEPOS_HH +#define OT_LAYOUT_GPOS_CURSIVEPOS_HH + +#include "CursivePosFormat1.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct CursivePos +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + CursivePosFormat1 format1; + } u; + + public: + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); + default:return_trace (c->default_return_value ()); + } + } +}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_CURSIVEPOS_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/CursivePosFormat1.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/CursivePosFormat1.hh new file mode 100644 index 00000000000..7c42c3f777a --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/CursivePosFormat1.hh @@ -0,0 +1,309 @@ +#ifndef OT_LAYOUT_GPOS_CURSIVEPOSFORMAT1_HH +#define OT_LAYOUT_GPOS_CURSIVEPOSFORMAT1_HH + +#include "Anchor.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct EntryExitRecord +{ + friend struct CursivePosFormat1; + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (entryAnchor.sanitize (c, base) && exitAnchor.sanitize (c, base)); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c, + const void *src_base) const + { + (src_base+entryAnchor).collect_variation_indices (c); + (src_base+exitAnchor).collect_variation_indices (c); + } + + bool subset (hb_subset_context_t *c, + const void *src_base) const + { + TRACE_SERIALIZE (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + bool ret = false; + ret |= out->entryAnchor.serialize_subset (c, entryAnchor, src_base); + ret |= out->exitAnchor.serialize_subset (c, exitAnchor, src_base); + return_trace (ret); + } + + protected: + Offset16To + entryAnchor; /* Offset to EntryAnchor table--from + * beginning of CursivePos + * subtable--may be NULL */ + Offset16To + exitAnchor; /* Offset to ExitAnchor table--from + * beginning of CursivePos + * subtable--may be NULL */ + public: + DEFINE_SIZE_STATIC (4); +}; + +static void +reverse_cursive_minor_offset (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction, unsigned int new_parent) { + int chain = pos[i].attach_chain(), type = pos[i].attach_type(); + if (likely (!chain || 0 == (type & ATTACH_TYPE_CURSIVE))) + return; + + pos[i].attach_chain() = 0; + + unsigned int j = (int) i + chain; + + /* Stop if we see new parent in the chain. */ + if (j == new_parent) + return; + + reverse_cursive_minor_offset (pos, j, direction, new_parent); + + if (HB_DIRECTION_IS_HORIZONTAL (direction)) + pos[j].y_offset = -pos[i].y_offset; + else + pos[j].x_offset = -pos[i].x_offset; + + pos[j].attach_chain() = -chain; + pos[j].attach_type() = type; +} + + +struct CursivePosFormat1 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + Offset16To + coverage; /* Offset to Coverage table--from + * beginning of subtable */ + Array16Of + entryExitRecord; /* Array of EntryExit records--in + * Coverage Index order */ + public: + DEFINE_SIZE_ARRAY (6, entryExitRecord); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!coverage.sanitize (c, this))) + return_trace (false); + + if (c->lazy_some_gpos) + return_trace (entryExitRecord.sanitize_shallow (c)); + else + return_trace (entryExitRecord.sanitize (c, this)); + } + + bool intersects (const hb_set_t *glyphs) const + { return (this+coverage).intersects (glyphs); } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + + hb_zip (this+coverage, entryExitRecord) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + | hb_apply ([&] (const EntryExitRecord& record) { record.collect_variation_indices (c, this); }) + ; + } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { if (unlikely (!(this+coverage).collect_coverage (c->input))) return; } + + const Coverage &get_coverage () const { return this+coverage; } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + + const EntryExitRecord &this_record = entryExitRecord[(this+coverage).get_coverage (buffer->cur().codepoint)]; + if (!this_record.entryAnchor || + unlikely (!this_record.entryAnchor.sanitize (&c->sanitizer, this))) return_trace (false); + + hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + skippy_iter.reset_fast (buffer->idx); + unsigned unsafe_from; + if (unlikely (!skippy_iter.prev (&unsafe_from))) + { + buffer->unsafe_to_concat_from_outbuffer (unsafe_from, buffer->idx + 1); + return_trace (false); + } + + const EntryExitRecord &prev_record = entryExitRecord[(this+coverage).get_coverage (buffer->info[skippy_iter.idx].codepoint)]; + if (!prev_record.exitAnchor || + unlikely (!prev_record.exitAnchor.sanitize (&c->sanitizer, this))) + { + buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1); + return_trace (false); + } + + unsigned int i = skippy_iter.idx; + unsigned int j = buffer->idx; + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "cursive attaching glyph at %u to glyph at %u", + i, j); + } + + buffer->unsafe_to_break (i, j + 1); + float entry_x, entry_y, exit_x, exit_y; + (this+prev_record.exitAnchor).get_anchor (c, buffer->info[i].codepoint, &exit_x, &exit_y); + (this+this_record.entryAnchor).get_anchor (c, buffer->info[j].codepoint, &entry_x, &entry_y); + + hb_glyph_position_t *pos = buffer->pos; + + hb_position_t d; + /* Main-direction adjustment */ + switch (c->direction) { + case HB_DIRECTION_LTR: + pos[i].x_advance = roundf (exit_x) + pos[i].x_offset; + + d = roundf (entry_x) + pos[j].x_offset; + pos[j].x_advance -= d; + pos[j].x_offset -= d; + break; + case HB_DIRECTION_RTL: + d = roundf (exit_x) + pos[i].x_offset; + pos[i].x_advance -= d; + pos[i].x_offset -= d; + + pos[j].x_advance = roundf (entry_x) + pos[j].x_offset; + break; + case HB_DIRECTION_TTB: + pos[i].y_advance = roundf (exit_y) + pos[i].y_offset; + + d = roundf (entry_y) + pos[j].y_offset; + pos[j].y_advance -= d; + pos[j].y_offset -= d; + break; + case HB_DIRECTION_BTT: + d = roundf (exit_y) + pos[i].y_offset; + pos[i].y_advance -= d; + pos[i].y_offset -= d; + + pos[j].y_advance = roundf (entry_y); + break; + case HB_DIRECTION_INVALID: + default: + break; + } + + /* Cross-direction adjustment */ + + /* We attach child to parent (think graph theory and rooted trees whereas + * the root stays on baseline and each node aligns itself against its + * parent. + * + * Optimize things for the case of RightToLeft, as that's most common in + * Arabic. */ + unsigned int child = i; + unsigned int parent = j; + hb_position_t x_offset = roundf (entry_x - exit_x); + hb_position_t y_offset = roundf (entry_y - exit_y); + if (!(c->lookup_props & LookupFlag::RightToLeft)) + { + unsigned int k = child; + child = parent; + parent = k; + x_offset = -x_offset; + y_offset = -y_offset; + } + + /* If child was already connected to someone else, walk through its old + * chain and reverse the link direction, such that the whole tree of its + * previous connection now attaches to new parent. Watch out for case + * where new parent is on the path from old chain... + */ + reverse_cursive_minor_offset (pos, child, c->direction, parent); + + pos[child].attach_type() = ATTACH_TYPE_CURSIVE; + pos[child].attach_chain() = (int) parent - (int) child; + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; + if (likely (HB_DIRECTION_IS_HORIZONTAL (c->direction))) + pos[child].y_offset = y_offset; + else + pos[child].x_offset = x_offset; + + /* If parent was attached to child, separate them. + * https://github.com/harfbuzz/harfbuzz/issues/2469 + */ + if (unlikely (pos[parent].attach_chain() == -pos[child].attach_chain())) + { + pos[parent].attach_chain() = 0; + if (likely (HB_DIRECTION_IS_HORIZONTAL (c->direction))) + pos[parent].y_offset = 0; + else + pos[parent].x_offset = 0; + } + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "cursive attached glyph at %u to glyph at %u", + i, j); + } + + buffer->idx++; + return_trace (true); + } + + template + void serialize (hb_subset_context_t *c, + Iterator it, + const void *src_base) + { + if (unlikely (!c->serializer->extend_min ((*this)))) return; + this->format = 1; + this->entryExitRecord.len = it.len (); + + for (const EntryExitRecord& entry_record : + it + | hb_map (hb_second)) + entry_record.subset (c, src_base); + + auto glyphs = + + it + | hb_map_retains_sorting (hb_first) + ; + + coverage.serialize_serialize (c->serializer, glyphs); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + + auto it = + + hb_zip (this+coverage, entryExitRecord) + | hb_filter (glyphset, hb_first) + | hb_map_retains_sorting ([&] (hb_pair_t p) -> hb_pair_t + { return hb_pair (glyph_map[p.first], p.second);}) + ; + + bool ret = bool (it); + out->serialize (c, it, this); + return_trace (ret); + } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GPOS_CURSIVEPOSFORMAT1_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/ExtensionPos.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/ExtensionPos.hh new file mode 100644 index 00000000000..d1808adab40 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/ExtensionPos.hh @@ -0,0 +1,17 @@ +#ifndef OT_LAYOUT_GPOS_EXTENSIONPOS_HH +#define OT_LAYOUT_GPOS_EXTENSIONPOS_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct ExtensionPos : Extension +{ + typedef struct PosLookupSubTable SubTable; +}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_EXTENSIONPOS_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/GPOS.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/GPOS.hh new file mode 100644 index 00000000000..f4af98b25fd --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/GPOS.hh @@ -0,0 +1,171 @@ +#ifndef OT_LAYOUT_GPOS_GPOS_HH +#define OT_LAYOUT_GPOS_GPOS_HH + +#include "../../../hb-ot-layout-common.hh" +#include "../../../hb-ot-layout-gsubgpos.hh" +#include "Common.hh" +#include "PosLookup.hh" + +namespace OT { + +using Layout::GPOS_impl::PosLookup; + +namespace Layout { + +static void +propagate_attachment_offsets (hb_glyph_position_t *pos, + unsigned int len, + unsigned int i, + hb_direction_t direction, + unsigned nesting_level = HB_MAX_NESTING_LEVEL); + +/* + * GPOS -- Glyph Positioning + * https://docs.microsoft.com/en-us/typography/opentype/spec/gpos + */ + +struct GPOS : GSUBGPOS +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_GPOS; + + using Lookup = PosLookup; + + const PosLookup& get_lookup (unsigned int i) const + { return static_cast (GSUBGPOS::get_lookup (i)); } + + static inline void position_start (hb_font_t *font, hb_buffer_t *buffer); + static inline void position_finish_advances (hb_font_t *font, hb_buffer_t *buffer); + static inline void position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer); + + bool subset (hb_subset_context_t *c) const + { + hb_subset_layout_context_t l (c, tableTag); + return GSUBGPOS::subset (&l); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (GSUBGPOS::sanitize (c)); + } + + HB_INTERNAL bool is_blocklisted (hb_blob_t *blob, + hb_face_t *face) const; + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + for (unsigned i = 0; i < GSUBGPOS::get_lookup_count (); i++) + { + if (!c->gpos_lookups->has (i)) continue; + const PosLookup &l = get_lookup (i); + l.dispatch (c); + } + } + + void closure_lookups (hb_face_t *face, + const hb_set_t *glyphs, + hb_set_t *lookup_indexes /* IN/OUT */) const + { GSUBGPOS::closure_lookups (face, glyphs, lookup_indexes); } + + typedef GSUBGPOS::accelerator_t accelerator_t; +}; + + +static void +propagate_attachment_offsets (hb_glyph_position_t *pos, + unsigned int len, + unsigned int i, + hb_direction_t direction, + unsigned nesting_level) +{ + /* Adjusts offsets of attached glyphs (both cursive and mark) to accumulate + * offset of glyph they are attached to. */ + int chain = pos[i].attach_chain(), type = pos[i].attach_type(); + if (likely (!chain)) + return; + + pos[i].attach_chain() = 0; + + unsigned int j = (int) i + chain; + + if (unlikely (j >= len)) + return; + + if (unlikely (!nesting_level)) + return; + + propagate_attachment_offsets (pos, len, j, direction, nesting_level - 1); + + assert (!!(type & GPOS_impl::ATTACH_TYPE_MARK) ^ !!(type & GPOS_impl::ATTACH_TYPE_CURSIVE)); + + if (type & GPOS_impl::ATTACH_TYPE_CURSIVE) + { + if (HB_DIRECTION_IS_HORIZONTAL (direction)) + pos[i].y_offset += pos[j].y_offset; + else + pos[i].x_offset += pos[j].x_offset; + } + else /*if (type & GPOS_impl::ATTACH_TYPE_MARK)*/ + { + pos[i].x_offset += pos[j].x_offset; + pos[i].y_offset += pos[j].y_offset; + + assert (j < i); + if (HB_DIRECTION_IS_FORWARD (direction)) + for (unsigned int k = j; k < i; k++) { + pos[i].x_offset -= pos[k].x_advance; + pos[i].y_offset -= pos[k].y_advance; + } + else + for (unsigned int k = j + 1; k < i + 1; k++) { + pos[i].x_offset += pos[k].x_advance; + pos[i].y_offset += pos[k].y_advance; + } + } +} + +void +GPOS::position_start (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer) +{ + unsigned int count = buffer->len; + for (unsigned int i = 0; i < count; i++) + buffer->pos[i].attach_chain() = buffer->pos[i].attach_type() = 0; +} + +void +GPOS::position_finish_advances (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer HB_UNUSED) +{ + //_hb_buffer_assert_gsubgpos_vars (buffer); +} + +void +GPOS::position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer) +{ + _hb_buffer_assert_gsubgpos_vars (buffer); + + unsigned int len; + hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (buffer, &len); + hb_direction_t direction = buffer->props.direction; + + /* Handle attachments */ + if (buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT) + for (unsigned i = 0; i < len; i++) + propagate_attachment_offsets (pos, len, i, direction); + + if (unlikely (font->slant)) + { + for (unsigned i = 0; i < len; i++) + if (unlikely (pos[i].y_offset)) + pos[i].x_offset += roundf (font->slant_xy * pos[i].y_offset); + } +} + +} + +struct GPOS_accelerator_t : Layout::GPOS::accelerator_t { + GPOS_accelerator_t (hb_face_t *face) : Layout::GPOS::accelerator_t (face) {} +}; + +} + +#endif /* OT_LAYOUT_GPOS_GPOS_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/LigatureArray.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/LigatureArray.hh new file mode 100644 index 00000000000..59cca40aadc --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/LigatureArray.hh @@ -0,0 +1,57 @@ +#ifndef OT_LAYOUT_GPOS_LIGATUREARRAY_HH +#define OT_LAYOUT_GPOS_LIGATUREARRAY_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + + +typedef AnchorMatrix LigatureAttach; /* component-major-- + * in order of writing direction--, + * mark-minor-- + * ordered by class--zero-based. */ + +/* Array of LigatureAttach tables ordered by LigatureCoverage Index */ +struct LigatureArray : List16OfOffset16To +{ + template + bool subset (hb_subset_context_t *c, + Iterator coverage, + unsigned class_count, + const hb_map_t *klass_mapping) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + + auto *out = c->serializer->start_embed (this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + bool ret = false; + for (const auto _ : + hb_zip (coverage, *this) + | hb_filter (glyphset, hb_first)) + { + auto *matrix = out->serialize_append (c->serializer); + if (unlikely (!matrix)) return_trace (false); + + const LigatureAttach& src = (this + _.second); + auto indexes = + + hb_range (src.rows * class_count) + | hb_filter ([=] (unsigned index) { return klass_mapping->has (index % class_count); }) + ; + ret |= matrix->serialize_subset (c, + _.second, + this, + src.rows, + indexes); + } + return_trace (ret); + } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GPOS_LIGATUREARRAY_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkArray.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkArray.hh new file mode 100644 index 00000000000..0887cc158b1 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkArray.hh @@ -0,0 +1,128 @@ +#ifndef OT_LAYOUT_GPOS_MARKARRAY_HH +#define OT_LAYOUT_GPOS_MARKARRAY_HH + +#include "AnchorMatrix.hh" +#include "MarkRecord.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct MarkArray : Array16Of /* Array of MarkRecords--in Coverage order */ +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (Array16Of::sanitize (c, this)); + } + + bool apply (hb_ot_apply_context_t *c, + unsigned int mark_index, unsigned int glyph_index, + const AnchorMatrix &anchors, unsigned int class_count, + unsigned int glyph_pos) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + const MarkRecord &record = Array16Of::operator[](mark_index); + unsigned int mark_class = record.klass; + + const Anchor& mark_anchor = this + record.markAnchor; + bool found; + const Anchor& glyph_anchor = anchors.get_anchor (c, glyph_index, mark_class, class_count, &found); + /* If this subtable doesn't have an anchor for this base and this class, + * return false such that the subsequent subtables have a chance at it. */ + if (unlikely (!found)) return_trace (false); + + float mark_x, mark_y, base_x, base_y; + + buffer->unsafe_to_break (glyph_pos, buffer->idx + 1); + mark_anchor.get_anchor (c, buffer->cur().codepoint, &mark_x, &mark_y); + glyph_anchor.get_anchor (c, buffer->info[glyph_pos].codepoint, &base_x, &base_y); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "attaching mark glyph at %u to glyph at %u", + c->buffer->idx, glyph_pos); + } + + hb_glyph_position_t &o = buffer->cur_pos(); + o.x_offset = roundf (base_x - mark_x); + o.y_offset = roundf (base_y - mark_y); + o.attach_type() = ATTACH_TYPE_MARK; + o.attach_chain() = (int) glyph_pos - (int) buffer->idx; + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "attached mark glyph at %u to glyph at %u", + c->buffer->idx, glyph_pos); + } + + buffer->idx++; + return_trace (true); + } + + template + bool subset (hb_subset_context_t *c, + Iterator coverage, + const hb_map_t *klass_mapping) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + + auto* out = c->serializer->start_embed (this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + auto mark_iter = + + hb_zip (coverage, this->iter ()) + | hb_filter (glyphset, hb_first) + | hb_map (hb_second) + ; + + bool ret = false; + unsigned new_length = 0; + for (const auto& mark_record : mark_iter) { + ret |= mark_record.subset (c, this, klass_mapping); + new_length++; + } + + if (unlikely (!c->serializer->check_assign (out->len, new_length, + HB_SERIALIZE_ERROR_ARRAY_OVERFLOW))) + return_trace (false); + + return_trace (ret); + } +}; + +HB_INTERNAL inline +void Markclass_closure_and_remap_indexes (const Coverage &mark_coverage, + const MarkArray &mark_array, + const hb_set_t &glyphset, + hb_map_t* klass_mapping /* INOUT */) +{ + hb_set_t orig_classes; + + + hb_zip (mark_coverage, mark_array) + | hb_filter (glyphset, hb_first) + | hb_map (hb_second) + | hb_map (&MarkRecord::get_class) + | hb_sink (orig_classes) + ; + + unsigned idx = 0; + for (auto klass : orig_classes.iter ()) + { + if (klass_mapping->has (klass)) continue; + klass_mapping->set (klass, idx); + idx++; + } +} + +} +} +} + +#endif /* OT_LAYOUT_GPOS_MARKARRAY_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkBasePos.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkBasePos.hh new file mode 100644 index 00000000000..cd2fc7ccfdd --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkBasePos.hh @@ -0,0 +1,41 @@ +#ifndef OT_LAYOUT_GPOS_MARKBASEPOS_HH +#define OT_LAYOUT_GPOS_MARKBASEPOS_HH + +#include "MarkBasePosFormat1.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct MarkBasePos +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + MarkBasePosFormat1_2 format1; +#ifndef HB_NO_BEYOND_64K + MarkBasePosFormat1_2 format2; +#endif + } u; + + public: + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); +#ifndef HB_NO_BEYOND_64K + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); +#endif + default:return_trace (c->default_return_value ()); + } + } +}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_MARKBASEPOS_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkBasePosFormat1.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkBasePosFormat1.hh new file mode 100644 index 00000000000..1b8f3c80a9a --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkBasePosFormat1.hh @@ -0,0 +1,243 @@ +#ifndef OT_LAYOUT_GPOS_MARKBASEPOSFORMAT1_HH +#define OT_LAYOUT_GPOS_MARKBASEPOSFORMAT1_HH + +#include "MarkArray.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +typedef AnchorMatrix BaseArray; /* base-major-- + * in order of BaseCoverage Index--, + * mark-minor-- + * ordered by class--zero-based. */ + +template +struct MarkBasePosFormat1_2 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + typename Types::template OffsetTo + markCoverage; /* Offset to MarkCoverage table--from + * beginning of MarkBasePos subtable */ + typename Types::template OffsetTo + baseCoverage; /* Offset to BaseCoverage table--from + * beginning of MarkBasePos subtable */ + HBUINT16 classCount; /* Number of classes defined for marks */ + typename Types::template OffsetTo + markArray; /* Offset to MarkArray table--from + * beginning of MarkBasePos subtable */ + typename Types::template OffsetTo + baseArray; /* Offset to BaseArray table--from + * beginning of MarkBasePos subtable */ + + public: + DEFINE_SIZE_STATIC (4 + 4 * Types::size); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + markCoverage.sanitize (c, this) && + baseCoverage.sanitize (c, this) && + markArray.sanitize (c, this) && + baseArray.sanitize (c, this, (unsigned int) classCount)); + } + + bool intersects (const hb_set_t *glyphs) const + { + return (this+markCoverage).intersects (glyphs) && + (this+baseCoverage).intersects (glyphs); + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + + hb_zip (this+markCoverage, this+markArray) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + | hb_apply ([&] (const MarkRecord& record) { record.collect_variation_indices (c, &(this+markArray)); }) + ; + + hb_map_t klass_mapping; + Markclass_closure_and_remap_indexes (this+markCoverage, this+markArray, *c->glyph_set, &klass_mapping); + + unsigned basecount = (this+baseArray).rows; + auto base_iter = + + hb_zip (this+baseCoverage, hb_range (basecount)) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + ; + + hb_sorted_vector_t base_indexes; + for (const unsigned row : base_iter) + { + + hb_range ((unsigned) classCount) + | hb_filter (klass_mapping) + | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; }) + | hb_sink (base_indexes) + ; + } + (this+baseArray).collect_variation_indices (c, base_indexes.iter ()); + } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + if (unlikely (!(this+markCoverage).collect_coverage (c->input))) return; + if (unlikely (!(this+baseCoverage).collect_coverage (c->input))) return; + } + + const Coverage &get_coverage () const { return this+markCoverage; } + + static inline bool accept (hb_buffer_t *buffer, unsigned idx) + { + /* We only want to attach to the first of a MultipleSubst sequence. + * https://github.com/harfbuzz/harfbuzz/issues/740 + * Reject others... + * ...but stop if we find a mark in the MultipleSubst sequence: + * https://github.com/harfbuzz/harfbuzz/issues/1020 */ + return !_hb_glyph_info_multiplied (&buffer->info[idx]) || + 0 == _hb_glyph_info_get_lig_comp (&buffer->info[idx]) || + (idx == 0 || + _hb_glyph_info_is_mark (&buffer->info[idx - 1]) || + !_hb_glyph_info_multiplied (&buffer->info[idx - 1]) || + _hb_glyph_info_get_lig_id (&buffer->info[idx]) != + _hb_glyph_info_get_lig_id (&buffer->info[idx - 1]) || + _hb_glyph_info_get_lig_comp (&buffer->info[idx]) != + _hb_glyph_info_get_lig_comp (&buffer->info[idx - 1]) + 1 + ); + } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + unsigned int mark_index = (this+markCoverage).get_coverage (buffer->cur().codepoint); + if (likely (mark_index == NOT_COVERED)) return_trace (false); + + /* Now we search backwards for a non-mark glyph. + * We don't use skippy_iter.prev() to avoid O(n^2) behavior. */ + + hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks); + + if (c->last_base_until > buffer->idx) + { + c->last_base_until = 0; + c->last_base = -1; + } + unsigned j; + for (j = buffer->idx; j > c->last_base_until; j--) + { + auto match = skippy_iter.match (buffer->info[j - 1]); + if (match == skippy_iter.MATCH) + { + // https://github.com/harfbuzz/harfbuzz/issues/4124 + if (!accept (buffer, j - 1) && + NOT_COVERED == (this+baseCoverage).get_coverage (buffer->info[j - 1].codepoint)) + match = skippy_iter.SKIP; + } + if (match == skippy_iter.MATCH) + { + c->last_base = (signed) j - 1; + break; + } + } + c->last_base_until = buffer->idx; + if (c->last_base == -1) + { + buffer->unsafe_to_concat_from_outbuffer (0, buffer->idx + 1); + return_trace (false); + } + + unsigned idx = (unsigned) c->last_base; + + /* Checking that matched glyph is actually a base glyph by GDEF is too strong; disabled */ + //if (!_hb_glyph_info_is_base_glyph (&buffer->info[idx])) { return_trace (false); } + + unsigned int base_index = (this+baseCoverage).get_coverage (buffer->info[idx].codepoint); + if (base_index == NOT_COVERED) + { + buffer->unsafe_to_concat_from_outbuffer (idx, buffer->idx + 1); + return_trace (false); + } + + return_trace ((this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, idx)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + hb_map_t klass_mapping; + Markclass_closure_and_remap_indexes (this+markCoverage, this+markArray, glyphset, &klass_mapping); + + if (!klass_mapping.get_population ()) return_trace (false); + out->classCount = klass_mapping.get_population (); + + auto mark_iter = + + hb_zip (this+markCoverage, this+markArray) + | hb_filter (glyphset, hb_first) + ; + + hb_sorted_vector_t new_coverage; + + mark_iter + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + + if (!out->markCoverage.serialize_serialize (c->serializer, new_coverage.iter ())) + return_trace (false); + + if (unlikely (!out->markArray.serialize_subset (c, markArray, this, + (this+markCoverage).iter (), + &klass_mapping))) + return_trace (false); + + unsigned basecount = (this+baseArray).rows; + auto base_iter = + + hb_zip (this+baseCoverage, hb_range (basecount)) + | hb_filter (glyphset, hb_first) + ; + + new_coverage.reset (); + + base_iter + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + + if (!out->baseCoverage.serialize_serialize (c->serializer, new_coverage.iter ())) + return_trace (false); + + hb_sorted_vector_t base_indexes; + for (const unsigned row : + base_iter + | hb_map (hb_second)) + { + + hb_range ((unsigned) classCount) + | hb_filter (klass_mapping) + | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; }) + | hb_sink (base_indexes) + ; + } + + return_trace (out->baseArray.serialize_subset (c, baseArray, this, + base_iter.len (), + base_indexes.iter ())); + } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GPOS_MARKBASEPOSFORMAT1_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkLigPos.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkLigPos.hh new file mode 100644 index 00000000000..739c325411c --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkLigPos.hh @@ -0,0 +1,41 @@ +#ifndef OT_LAYOUT_GPOS_MARKLIGPOS_HH +#define OT_LAYOUT_GPOS_MARKLIGPOS_HH + +#include "MarkLigPosFormat1.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct MarkLigPos +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + MarkLigPosFormat1_2 format1; +#ifndef HB_NO_BEYOND_64K + MarkLigPosFormat1_2 format2; +#endif + } u; + + public: + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); +#ifndef HB_NO_BEYOND_64K + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); +#endif + default:return_trace (c->default_return_value ()); + } + } +}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_MARKLIGPOS_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkLigPosFormat1.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkLigPosFormat1.hh new file mode 100644 index 00000000000..d6bee277c70 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkLigPosFormat1.hh @@ -0,0 +1,224 @@ +#ifndef OT_LAYOUT_GPOS_MARKLIGPOSFORMAT1_HH +#define OT_LAYOUT_GPOS_MARKLIGPOSFORMAT1_HH + +#include "LigatureArray.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + + +template +struct MarkLigPosFormat1_2 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + typename Types::template OffsetTo + markCoverage; /* Offset to Mark Coverage table--from + * beginning of MarkLigPos subtable */ + typename Types::template OffsetTo + ligatureCoverage; /* Offset to Ligature Coverage + * table--from beginning of MarkLigPos + * subtable */ + HBUINT16 classCount; /* Number of defined mark classes */ + typename Types::template OffsetTo + markArray; /* Offset to MarkArray table--from + * beginning of MarkLigPos subtable */ + typename Types::template OffsetTo + ligatureArray; /* Offset to LigatureArray table--from + * beginning of MarkLigPos subtable */ + public: + DEFINE_SIZE_STATIC (4 + 4 * Types::size); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + markCoverage.sanitize (c, this) && + ligatureCoverage.sanitize (c, this) && + markArray.sanitize (c, this) && + ligatureArray.sanitize (c, this, (unsigned int) classCount)); + } + + bool intersects (const hb_set_t *glyphs) const + { + return (this+markCoverage).intersects (glyphs) && + (this+ligatureCoverage).intersects (glyphs); + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + + hb_zip (this+markCoverage, this+markArray) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + | hb_apply ([&] (const MarkRecord& record) { record.collect_variation_indices (c, &(this+markArray)); }) + ; + + hb_map_t klass_mapping; + Markclass_closure_and_remap_indexes (this+markCoverage, this+markArray, *c->glyph_set, &klass_mapping); + + unsigned ligcount = (this+ligatureArray).len; + auto lig_iter = + + hb_zip (this+ligatureCoverage, hb_range (ligcount)) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + ; + + const LigatureArray& lig_array = this+ligatureArray; + for (const unsigned i : lig_iter) + { + hb_sorted_vector_t lig_indexes; + unsigned row_count = lig_array[i].rows; + for (unsigned row : + hb_range (row_count)) + { + + hb_range ((unsigned) classCount) + | hb_filter (klass_mapping) + | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; }) + | hb_sink (lig_indexes) + ; + } + + lig_array[i].collect_variation_indices (c, lig_indexes.iter ()); + } + } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + if (unlikely (!(this+markCoverage).collect_coverage (c->input))) return; + if (unlikely (!(this+ligatureCoverage).collect_coverage (c->input))) return; + } + + const Coverage &get_coverage () const { return this+markCoverage; } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + unsigned int mark_index = (this+markCoverage).get_coverage (buffer->cur().codepoint); + if (likely (mark_index == NOT_COVERED)) return_trace (false); + + /* Now we search backwards for a non-mark glyph */ + + hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks); + + if (c->last_base_until > buffer->idx) + { + c->last_base_until = 0; + c->last_base = -1; + } + unsigned j; + for (j = buffer->idx; j > c->last_base_until; j--) + { + auto match = skippy_iter.match (buffer->info[j - 1]); + if (match == skippy_iter.MATCH) + { + c->last_base = (signed) j - 1; + break; + } + } + c->last_base_until = buffer->idx; + if (c->last_base == -1) + { + buffer->unsafe_to_concat_from_outbuffer (0, buffer->idx + 1); + return_trace (false); + } + + unsigned idx = (unsigned) c->last_base; + + /* Checking that matched glyph is actually a ligature by GDEF is too strong; disabled */ + //if (!_hb_glyph_info_is_ligature (&buffer->info[idx])) { return_trace (false); } + + unsigned int lig_index = (this+ligatureCoverage).get_coverage (buffer->info[idx].codepoint); + if (lig_index == NOT_COVERED) + { + buffer->unsafe_to_concat_from_outbuffer (idx, buffer->idx + 1); + return_trace (false); + } + + const LigatureArray& lig_array = this+ligatureArray; + const LigatureAttach& lig_attach = lig_array[lig_index]; + + /* Find component to attach to */ + unsigned int comp_count = lig_attach.rows; + if (unlikely (!comp_count)) + { + buffer->unsafe_to_concat_from_outbuffer (idx, buffer->idx + 1); + return_trace (false); + } + + /* We must now check whether the ligature ID of the current mark glyph + * is identical to the ligature ID of the found ligature. If yes, we + * can directly use the component index. If not, we attach the mark + * glyph to the last component of the ligature. */ + unsigned int comp_index; + unsigned int lig_id = _hb_glyph_info_get_lig_id (&buffer->info[idx]); + unsigned int mark_id = _hb_glyph_info_get_lig_id (&buffer->cur()); + unsigned int mark_comp = _hb_glyph_info_get_lig_comp (&buffer->cur()); + if (lig_id && lig_id == mark_id && mark_comp > 0) + comp_index = hb_min (comp_count, _hb_glyph_info_get_lig_comp (&buffer->cur())) - 1; + else + comp_index = comp_count - 1; + + return_trace ((this+markArray).apply (c, mark_index, comp_index, lig_attach, classCount, idx)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = c->plan->glyph_map_gsub; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + hb_map_t klass_mapping; + Markclass_closure_and_remap_indexes (this+markCoverage, this+markArray, glyphset, &klass_mapping); + + if (!klass_mapping.get_population ()) return_trace (false); + out->classCount = klass_mapping.get_population (); + + auto mark_iter = + + hb_zip (this+markCoverage, this+markArray) + | hb_filter (glyphset, hb_first) + ; + + auto new_mark_coverage = + + mark_iter + | hb_map_retains_sorting (hb_first) + | hb_map_retains_sorting (glyph_map) + ; + + if (!out->markCoverage.serialize_serialize (c->serializer, new_mark_coverage)) + return_trace (false); + + if (unlikely (!out->markArray.serialize_subset (c, markArray, this, + (this+markCoverage).iter (), + &klass_mapping))) + return_trace (false); + + auto new_ligature_coverage = + + hb_iter (this + ligatureCoverage) + | hb_take ((this + ligatureArray).len) + | hb_map_retains_sorting (glyph_map) + | hb_filter ([] (hb_codepoint_t glyph) { return glyph != HB_MAP_VALUE_INVALID; }) + ; + + if (!out->ligatureCoverage.serialize_serialize (c->serializer, new_ligature_coverage)) + return_trace (false); + + return_trace (out->ligatureArray.serialize_subset (c, ligatureArray, this, + hb_iter (this+ligatureCoverage), + classCount, &klass_mapping)); + } + +}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_MARKLIGPOSFORMAT1_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkMarkPos.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkMarkPos.hh new file mode 100644 index 00000000000..cddd2a3d507 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkMarkPos.hh @@ -0,0 +1,42 @@ +#ifndef OT_LAYOUT_GPOS_MARKMARKPOS_HH +#define OT_LAYOUT_GPOS_MARKMARKPOS_HH + +#include "MarkMarkPosFormat1.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct MarkMarkPos +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + MarkMarkPosFormat1_2 format1; +#ifndef HB_NO_BEYOND_64K + MarkMarkPosFormat1_2 format2; +#endif + } u; + + public: + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); +#ifndef HB_NO_BEYOND_64K + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); +#endif + default:return_trace (c->default_return_value ()); + } + } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GPOS_MARKMARKPOS_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkMarkPosFormat1.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkMarkPosFormat1.hh new file mode 100644 index 00000000000..70cf071668e --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkMarkPosFormat1.hh @@ -0,0 +1,230 @@ +#ifndef OT_LAYOUT_GPOS_MARKMARKPOSFORMAT1_HH +#define OT_LAYOUT_GPOS_MARKMARKPOSFORMAT1_HH + +#include "MarkMarkPosFormat1.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +typedef AnchorMatrix Mark2Array; /* mark2-major-- + * in order of Mark2Coverage Index--, + * mark1-minor-- + * ordered by class--zero-based. */ + +template +struct MarkMarkPosFormat1_2 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + typename Types::template OffsetTo + mark1Coverage; /* Offset to Combining Mark1 Coverage + * table--from beginning of MarkMarkPos + * subtable */ + typename Types::template OffsetTo + mark2Coverage; /* Offset to Combining Mark2 Coverage + * table--from beginning of MarkMarkPos + * subtable */ + HBUINT16 classCount; /* Number of defined mark classes */ + typename Types::template OffsetTo + mark1Array; /* Offset to Mark1Array table--from + * beginning of MarkMarkPos subtable */ + typename Types::template OffsetTo + mark2Array; /* Offset to Mark2Array table--from + * beginning of MarkMarkPos subtable */ + public: + DEFINE_SIZE_STATIC (4 + 4 * Types::size); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + mark1Coverage.sanitize (c, this) && + mark2Coverage.sanitize (c, this) && + mark1Array.sanitize (c, this) && + mark2Array.sanitize (c, this, (unsigned int) classCount)); + } + + bool intersects (const hb_set_t *glyphs) const + { + return (this+mark1Coverage).intersects (glyphs) && + (this+mark2Coverage).intersects (glyphs); + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + + hb_zip (this+mark1Coverage, this+mark1Array) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + | hb_apply ([&] (const MarkRecord& record) { record.collect_variation_indices (c, &(this+mark1Array)); }) + ; + + hb_map_t klass_mapping; + Markclass_closure_and_remap_indexes (this+mark1Coverage, this+mark1Array, *c->glyph_set, &klass_mapping); + + unsigned mark2_count = (this+mark2Array).rows; + auto mark2_iter = + + hb_zip (this+mark2Coverage, hb_range (mark2_count)) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + ; + + hb_sorted_vector_t mark2_indexes; + for (const unsigned row : mark2_iter) + { + + hb_range ((unsigned) classCount) + | hb_filter (klass_mapping) + | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; }) + | hb_sink (mark2_indexes) + ; + } + (this+mark2Array).collect_variation_indices (c, mark2_indexes.iter ()); + } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + if (unlikely (!(this+mark1Coverage).collect_coverage (c->input))) return; + if (unlikely (!(this+mark2Coverage).collect_coverage (c->input))) return; + } + + const Coverage &get_coverage () const { return this+mark1Coverage; } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + unsigned int mark1_index = (this+mark1Coverage).get_coverage (buffer->cur().codepoint); + if (likely (mark1_index == NOT_COVERED)) return_trace (false); + + /* now we search backwards for a suitable mark glyph until a non-mark glyph */ + hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + skippy_iter.reset_fast (buffer->idx); + skippy_iter.set_lookup_props (c->lookup_props & ~(uint32_t)LookupFlag::IgnoreFlags); + unsigned unsafe_from; + if (unlikely (!skippy_iter.prev (&unsafe_from))) + { + buffer->unsafe_to_concat_from_outbuffer (unsafe_from, buffer->idx + 1); + return_trace (false); + } + + if (likely (!_hb_glyph_info_is_mark (&buffer->info[skippy_iter.idx]))) + { + buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1); + return_trace (false); + } + + unsigned int j = skippy_iter.idx; + + unsigned int id1 = _hb_glyph_info_get_lig_id (&buffer->cur()); + unsigned int id2 = _hb_glyph_info_get_lig_id (&buffer->info[j]); + unsigned int comp1 = _hb_glyph_info_get_lig_comp (&buffer->cur()); + unsigned int comp2 = _hb_glyph_info_get_lig_comp (&buffer->info[j]); + + if (likely (id1 == id2)) + { + if (id1 == 0) /* Marks belonging to the same base. */ + goto good; + else if (comp1 == comp2) /* Marks belonging to the same ligature component. */ + goto good; + } + else + { + /* If ligature ids don't match, it may be the case that one of the marks + * itself is a ligature. In which case match. */ + if ((id1 > 0 && !comp1) || (id2 > 0 && !comp2)) + goto good; + } + + /* Didn't match. */ + buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1); + return_trace (false); + + good: + unsigned int mark2_index = (this+mark2Coverage).get_coverage (buffer->info[j].codepoint); + if (mark2_index == NOT_COVERED) + { + buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1); + return_trace (false); + } + + return_trace ((this+mark1Array).apply (c, mark1_index, mark2_index, this+mark2Array, classCount, j)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + hb_map_t klass_mapping; + Markclass_closure_and_remap_indexes (this+mark1Coverage, this+mark1Array, glyphset, &klass_mapping); + + if (!klass_mapping.get_population ()) return_trace (false); + out->classCount = klass_mapping.get_population (); + + auto mark1_iter = + + hb_zip (this+mark1Coverage, this+mark1Array) + | hb_filter (glyphset, hb_first) + ; + + hb_sorted_vector_t new_coverage; + + mark1_iter + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + + if (!out->mark1Coverage.serialize_serialize (c->serializer, new_coverage.iter ())) + return_trace (false); + + if (unlikely (!out->mark1Array.serialize_subset (c, mark1Array, this, + (this+mark1Coverage).iter (), + &klass_mapping))) + return_trace (false); + + unsigned mark2count = (this+mark2Array).rows; + auto mark2_iter = + + hb_zip (this+mark2Coverage, hb_range (mark2count)) + | hb_filter (glyphset, hb_first) + ; + + new_coverage.reset (); + + mark2_iter + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + + if (!out->mark2Coverage.serialize_serialize (c->serializer, new_coverage.iter ())) + return_trace (false); + + hb_sorted_vector_t mark2_indexes; + for (const unsigned row : + mark2_iter + | hb_map (hb_second)) + { + + hb_range ((unsigned) classCount) + | hb_filter (klass_mapping) + | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; }) + | hb_sink (mark2_indexes) + ; + } + + return_trace (out->mark2Array.serialize_subset (c, mark2Array, this, + mark2_iter.len (), + mark2_indexes.iter ())); + + } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GPOS_MARKMARKPOSFORMAT1_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkRecord.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkRecord.hh new file mode 100644 index 00000000000..3d11c7773cf --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/MarkRecord.hh @@ -0,0 +1,51 @@ +#ifndef OT_LAYOUT_GPOS_MARKRECORD_HH +#define OT_LAYOUT_GPOS_MARKRECORD_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct MarkRecord +{ + friend struct MarkArray; + + public: + HBUINT16 klass; /* Class defined for this mark */ + Offset16To + markAnchor; /* Offset to Anchor table--from + * beginning of MarkArray table */ + public: + DEFINE_SIZE_STATIC (4); + + unsigned get_class () const { return (unsigned) klass; } + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && markAnchor.sanitize (c, base)); + } + + bool subset (hb_subset_context_t *c, + const void *src_base, + const hb_map_t *klass_mapping) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + out->klass = klass_mapping->get (klass); + return_trace (out->markAnchor.serialize_subset (c, markAnchor, src_base)); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c, + const void *src_base) const + { + (src_base+markAnchor).collect_variation_indices (c); + } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GPOS_MARKRECORD_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PairPos.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PairPos.hh new file mode 100644 index 00000000000..c13d4f48941 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PairPos.hh @@ -0,0 +1,46 @@ +#ifndef OT_LAYOUT_GPOS_PAIRPOS_HH +#define OT_LAYOUT_GPOS_PAIRPOS_HH + +#include "PairPosFormat1.hh" +#include "PairPosFormat2.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct PairPos +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + PairPosFormat1_3 format1; + PairPosFormat2_4 format2; +#ifndef HB_NO_BEYOND_64K + PairPosFormat1_3 format3; + PairPosFormat2_4 format4; +#endif + } u; + + public: + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); +#ifndef HB_NO_BEYOND_64K + case 3: return_trace (c->dispatch (u.format3, std::forward (ds)...)); + case 4: return_trace (c->dispatch (u.format4, std::forward (ds)...)); +#endif + default:return_trace (c->default_return_value ()); + } + } +}; + +} +} +} + +#endif // OT_LAYOUT_GPOS_PAIRPOS_HH diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PairPosFormat1.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PairPosFormat1.hh new file mode 100644 index 00000000000..e4a2006fb98 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PairPosFormat1.hh @@ -0,0 +1,217 @@ +#ifndef OT_LAYOUT_GPOS_PAIRPOSFORMAT1_HH +#define OT_LAYOUT_GPOS_PAIRPOSFORMAT1_HH + +#include "PairSet.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + + +template +struct PairPosFormat1_3 +{ + using PairSet = GPOS_impl::PairSet; + using PairValueRecord = GPOS_impl::PairValueRecord; + + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + typename Types::template OffsetTo + coverage; /* Offset to Coverage table--from + * beginning of subtable */ + ValueFormat valueFormat[2]; /* [0] Defines the types of data in + * ValueRecord1--for the first glyph + * in the pair--may be zero (0) */ + /* [1] Defines the types of data in + * ValueRecord2--for the second glyph + * in the pair--may be zero (0) */ + Array16Of> + pairSet; /* Array of PairSet tables + * ordered by Coverage Index */ + public: + DEFINE_SIZE_ARRAY (8 + Types::size, pairSet); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + + if (!c->check_struct (this)) return_trace (false); + + unsigned int len1 = valueFormat[0].get_len (); + unsigned int len2 = valueFormat[1].get_len (); + typename PairSet::sanitize_closure_t closure = + { + valueFormat, + len1, + PairSet::get_size (len1, len2) + }; + + return_trace (coverage.sanitize (c, this) && pairSet.sanitize (c, this, &closure)); + } + + bool intersects (const hb_set_t *glyphs) const + { + auto &cov = this+coverage; + + if (pairSet.len > glyphs->get_population () * hb_bit_storage ((unsigned) pairSet.len) / 4) + { + for (hb_codepoint_t g : glyphs->iter()) + { + unsigned i = cov.get_coverage (g); + if ((this+pairSet[i]).intersects (glyphs, valueFormat)) + return true; + } + return false; + } + + return + + hb_zip (cov, pairSet) + | hb_filter (*glyphs, hb_first) + | hb_map (hb_second) + | hb_map ([glyphs, this] (const typename Types::template OffsetTo &_) + { return (this+_).intersects (glyphs, valueFormat); }) + | hb_any + ; + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + if ((!valueFormat[0].has_device ()) && (!valueFormat[1].has_device ())) return; + + auto it = + + hb_zip (this+coverage, pairSet) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + ; + + if (!it) return; + + it + | hb_map (hb_add (this)) + | hb_apply ([&] (const PairSet& _) { _.collect_variation_indices (c, valueFormat); }) + ; + } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; + unsigned int count = pairSet.len; + for (unsigned int i = 0; i < count; i++) + (this+pairSet[i]).collect_glyphs (c, valueFormat); + } + + const Coverage &get_coverage () const { return this+coverage; } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + skippy_iter.reset_fast (buffer->idx); + unsigned unsafe_to; + if (unlikely (!skippy_iter.next (&unsafe_to))) + { + buffer->unsafe_to_concat (buffer->idx, unsafe_to); + return_trace (false); + } + + return_trace ((this+pairSet[index]).apply (c, valueFormat, skippy_iter.idx)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + out->valueFormat[0] = valueFormat[0]; + out->valueFormat[1] = valueFormat[1]; + if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING) + { + hb_pair_t newFormats = compute_effective_value_formats (glyphset); + out->valueFormat[0] = newFormats.first; + out->valueFormat[1] = newFormats.second; + } + + if (c->plan->all_axes_pinned) + { + out->valueFormat[0] = out->valueFormat[0].drop_device_table_flags (); + out->valueFormat[1] = out->valueFormat[1].drop_device_table_flags (); + } + + hb_sorted_vector_t new_coverage; + + + hb_zip (this+coverage, pairSet) + | hb_filter (glyphset, hb_first) + | hb_filter ([this, c, out] (const typename Types::template OffsetTo& _) + { + auto snap = c->serializer->snapshot (); + auto *o = out->pairSet.serialize_append (c->serializer); + if (unlikely (!o)) return false; + bool ret = o->serialize_subset (c, _, this, valueFormat, out->valueFormat); + if (!ret) + { + out->pairSet.pop (); + c->serializer->revert (snap); + } + return ret; + }, + hb_second) + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + + out->coverage.serialize_serialize (c->serializer, new_coverage.iter ()); + + return_trace (bool (new_coverage)); + } + + + hb_pair_t compute_effective_value_formats (const hb_set_t& glyphset) const + { + unsigned record_size = PairSet::get_size (valueFormat); + + unsigned format1 = 0; + unsigned format2 = 0; + for (const auto & _ : + + hb_zip (this+coverage, pairSet) + | hb_filter (glyphset, hb_first) + | hb_map (hb_second) + ) + { + const PairSet& set = (this + _); + const PairValueRecord *record = &set.firstPairValueRecord; + + unsigned count = set.len; + for (unsigned i = 0; i < count; i++) + { + if (record->intersects (glyphset)) + { + format1 = format1 | valueFormat[0].get_effective_format (record->get_values_1 ()); + format2 = format2 | valueFormat[1].get_effective_format (record->get_values_2 (valueFormat[0])); + } + record = &StructAtOffset (record, record_size); + } + + if (format1 == valueFormat[0] && format2 == valueFormat[1]) + break; + } + + return hb_pair (format1, format2); + } +}; + + +} +} +} + +#endif // OT_LAYOUT_GPOS_PAIRPOSFORMAT1_HH diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PairPosFormat2.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PairPosFormat2.hh new file mode 100644 index 00000000000..4adb1ef606c --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PairPosFormat2.hh @@ -0,0 +1,359 @@ +#ifndef OT_LAYOUT_GPOS_PAIRPOSFORMAT2_HH +#define OT_LAYOUT_GPOS_PAIRPOSFORMAT2_HH + +#include "ValueFormat.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +template +struct PairPosFormat2_4 +{ + protected: + HBUINT16 format; /* Format identifier--format = 2 */ + typename Types::template OffsetTo + coverage; /* Offset to Coverage table--from + * beginning of subtable */ + ValueFormat valueFormat1; /* ValueRecord definition--for the + * first glyph of the pair--may be zero + * (0) */ + ValueFormat valueFormat2; /* ValueRecord definition--for the + * second glyph of the pair--may be + * zero (0) */ + typename Types::template OffsetTo + classDef1; /* Offset to ClassDef table--from + * beginning of PairPos subtable--for + * the first glyph of the pair */ + typename Types::template OffsetTo + classDef2; /* Offset to ClassDef table--from + * beginning of PairPos subtable--for + * the second glyph of the pair */ + HBUINT16 class1Count; /* Number of classes in ClassDef1 + * table--includes Class0 */ + HBUINT16 class2Count; /* Number of classes in ClassDef2 + * table--includes Class0 */ + ValueRecord values; /* Matrix of value pairs: + * class1-major, class2-minor, + * Each entry has value1 and value2 */ + public: + DEFINE_SIZE_ARRAY (10 + 3 * Types::size, values); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!(c->check_struct (this) + && coverage.sanitize (c, this) + && classDef1.sanitize (c, this) + && classDef2.sanitize (c, this))) return_trace (false); + + unsigned int len1 = valueFormat1.get_len (); + unsigned int len2 = valueFormat2.get_len (); + unsigned int stride = HBUINT16::static_size * (len1 + len2); + unsigned int count = (unsigned int) class1Count * (unsigned int) class2Count; + return_trace (c->check_range ((const void *) values, + count, + stride) && + (c->lazy_some_gpos || + (valueFormat1.sanitize_values_stride_unsafe (c, this, &values[0], count, stride) && + valueFormat2.sanitize_values_stride_unsafe (c, this, &values[len1], count, stride)))); + } + + bool intersects (const hb_set_t *glyphs) const + { + return (this+coverage).intersects (glyphs) && + (this+classDef2).intersects (glyphs); + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + if (!intersects (c->glyph_set)) return; + if ((!valueFormat1.has_device ()) && (!valueFormat2.has_device ())) return; + + hb_set_t klass1_glyphs, klass2_glyphs; + if (!(this+classDef1).collect_coverage (&klass1_glyphs)) return; + if (!(this+classDef2).collect_coverage (&klass2_glyphs)) return; + + hb_set_t class1_set, class2_set; + for (const unsigned cp : + c->glyph_set->iter () | hb_filter (this + coverage)) + { + if (!klass1_glyphs.has (cp)) class1_set.add (0); + else + { + unsigned klass1 = (this+classDef1).get (cp); + class1_set.add (klass1); + } + } + + class2_set.add (0); + for (const unsigned cp : + c->glyph_set->iter () | hb_filter (klass2_glyphs)) + { + unsigned klass2 = (this+classDef2).get (cp); + class2_set.add (klass2); + } + + if (class1_set.is_empty () + || class2_set.is_empty () + || (class2_set.get_population() == 1 && class2_set.has(0))) + return; + + unsigned len1 = valueFormat1.get_len (); + unsigned len2 = valueFormat2.get_len (); + const hb_array_t values_array = values.as_array ((unsigned)class1Count * (unsigned) class2Count * (len1 + len2)); + for (const unsigned class1_idx : class1_set.iter ()) + { + for (const unsigned class2_idx : class2_set.iter ()) + { + unsigned start_offset = (class1_idx * (unsigned) class2Count + class2_idx) * (len1 + len2); + if (valueFormat1.has_device ()) + valueFormat1.collect_variation_indices (c, this, values_array.sub_array (start_offset, len1)); + + if (valueFormat2.has_device ()) + valueFormat2.collect_variation_indices (c, this, values_array.sub_array (start_offset+len1, len2)); + } + } + } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; + if (unlikely (!(this+classDef2).collect_coverage (c->input))) return; + } + + const Coverage &get_coverage () const { return this+coverage; } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + skippy_iter.reset_fast (buffer->idx); + unsigned unsafe_to; + if (unlikely (!skippy_iter.next (&unsafe_to))) + { + buffer->unsafe_to_concat (buffer->idx, unsafe_to); + return_trace (false); + } + + unsigned int klass2 = (this+classDef2).get_class (buffer->info[skippy_iter.idx].codepoint); + if (!klass2) + { + buffer->unsafe_to_concat (buffer->idx, skippy_iter.idx + 1); + return_trace (false); + } + + unsigned int klass1 = (this+classDef1).get_class (buffer->cur().codepoint); + if (unlikely (klass1 >= class1Count || klass2 >= class2Count)) + { + buffer->unsafe_to_concat (buffer->idx, skippy_iter.idx + 1); + return_trace (false); + } + + unsigned int len1 = valueFormat1.get_len (); + unsigned int len2 = valueFormat2.get_len (); + unsigned int record_len = len1 + len2; + + const Value *v = &values[record_len * (klass1 * class2Count + klass2)]; + + bool applied_first = false, applied_second = false; + + + /* Isolate simple kerning and apply it half to each side. + * Results in better cursor positioning / underline drawing. + * + * Disabled, because causes issues... :-( + * https://github.com/harfbuzz/harfbuzz/issues/3408 + * https://github.com/harfbuzz/harfbuzz/pull/3235#issuecomment-1029814978 + */ +#ifndef HB_SPLIT_KERN + if (false) +#endif + { + if (!len2) + { + const hb_direction_t dir = buffer->props.direction; + const bool horizontal = HB_DIRECTION_IS_HORIZONTAL (dir); + const bool backward = HB_DIRECTION_IS_BACKWARD (dir); + unsigned mask = horizontal ? ValueFormat::xAdvance : ValueFormat::yAdvance; + if (backward) + mask |= mask >> 2; /* Add eg. xPlacement in RTL. */ + /* Add Devices. */ + mask |= mask << 4; + + if (valueFormat1 & ~mask) + goto bail; + + /* Is simple kern. Apply value on an empty position slot, + * then split it between sides. */ + + hb_glyph_position_t pos{}; + if (valueFormat1.apply_value (c, this, v, pos)) + { + hb_position_t *src = &pos.x_advance; + hb_position_t *dst1 = &buffer->cur_pos().x_advance; + hb_position_t *dst2 = &buffer->pos[skippy_iter.idx].x_advance; + unsigned i = horizontal ? 0 : 1; + + hb_position_t kern = src[i]; + hb_position_t kern1 = kern >> 1; + hb_position_t kern2 = kern - kern1; + + if (!backward) + { + dst1[i] += kern1; + dst2[i] += kern2; + dst2[i + 2] += kern2; + } + else + { + dst1[i] += kern1; + dst1[i + 2] += src[i + 2] - kern2; + dst2[i] += kern2; + } + + applied_first = applied_second = kern != 0; + goto success; + } + goto boring; + } + } + bail: + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "try kerning glyphs at %u,%u", + c->buffer->idx, skippy_iter.idx); + } + + applied_first = len1 && valueFormat1.apply_value (c, this, v, buffer->cur_pos()); + applied_second = len2 && valueFormat2.apply_value (c, this, v + len1, buffer->pos[skippy_iter.idx]); + + if (applied_first || applied_second) + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "kerned glyphs at %u,%u", + c->buffer->idx, skippy_iter.idx); + } + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "tried kerning glyphs at %u,%u", + c->buffer->idx, skippy_iter.idx); + } + + success: + if (applied_first || applied_second) + buffer->unsafe_to_break (buffer->idx, skippy_iter.idx + 1); + else + boring: + buffer->unsafe_to_concat (buffer->idx, skippy_iter.idx + 1); + + if (len2) + { + skippy_iter.idx++; + // https://github.com/harfbuzz/harfbuzz/issues/3824 + // https://github.com/harfbuzz/harfbuzz/issues/3888#issuecomment-1326781116 + buffer->unsafe_to_break (buffer->idx, skippy_iter.idx + 1); + } + + buffer->idx = skippy_iter.idx; + + return_trace (true); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + hb_map_t klass1_map; + out->classDef1.serialize_subset (c, classDef1, this, &klass1_map, true, true, &(this + coverage)); + out->class1Count = klass1_map.get_population (); + + hb_map_t klass2_map; + out->classDef2.serialize_subset (c, classDef2, this, &klass2_map, true, false); + out->class2Count = klass2_map.get_population (); + + unsigned len1 = valueFormat1.get_len (); + unsigned len2 = valueFormat2.get_len (); + + hb_pair_t newFormats = hb_pair (valueFormat1, valueFormat2); + if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING) + newFormats = compute_effective_value_formats (klass1_map, klass2_map); + + out->valueFormat1 = newFormats.first; + out->valueFormat2 = newFormats.second; + + if (c->plan->all_axes_pinned) + { + out->valueFormat1 = out->valueFormat1.drop_device_table_flags (); + out->valueFormat2 = out->valueFormat2.drop_device_table_flags (); + } + + unsigned total_len = len1 + len2; + hb_vector_t class2_idxs (+ hb_range ((unsigned) class2Count) | hb_filter (klass2_map)); + for (unsigned class1_idx : + hb_range ((unsigned) class1Count) | hb_filter (klass1_map)) + { + for (unsigned class2_idx : class2_idxs) + { + unsigned idx = (class1_idx * (unsigned) class2Count + class2_idx) * total_len; + valueFormat1.copy_values (c->serializer, out->valueFormat1, this, &values[idx], &c->plan->layout_variation_idx_delta_map); + valueFormat2.copy_values (c->serializer, out->valueFormat2, this, &values[idx + len1], &c->plan->layout_variation_idx_delta_map); + } + } + + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto it = + + hb_iter (this+coverage) + | hb_filter (glyphset) + | hb_map_retains_sorting (glyph_map) + ; + + out->coverage.serialize_serialize (c->serializer, it); + return_trace (out->class1Count && out->class2Count && bool (it)); + } + + + hb_pair_t compute_effective_value_formats (const hb_map_t& klass1_map, + const hb_map_t& klass2_map) const + { + unsigned len1 = valueFormat1.get_len (); + unsigned len2 = valueFormat2.get_len (); + unsigned record_size = len1 + len2; + + unsigned format1 = 0; + unsigned format2 = 0; + + for (unsigned class1_idx : + hb_range ((unsigned) class1Count) | hb_filter (klass1_map)) + { + for (unsigned class2_idx : + hb_range ((unsigned) class2Count) | hb_filter (klass2_map)) + { + unsigned idx = (class1_idx * (unsigned) class2Count + class2_idx) * record_size; + format1 = format1 | valueFormat1.get_effective_format (&values[idx]); + format2 = format2 | valueFormat2.get_effective_format (&values[idx + len1]); + } + + if (format1 == valueFormat1 && format2 == valueFormat2) + break; + } + + return hb_pair (format1, format2); + } +}; + +} +} +} + +#endif // OT_LAYOUT_GPOS_PAIRPOSFORMAT2_HH diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PairSet.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PairSet.hh new file mode 100644 index 00000000000..db301bb8160 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PairSet.hh @@ -0,0 +1,208 @@ +#ifndef OT_LAYOUT_GPOS_PAIRSET_HH +#define OT_LAYOUT_GPOS_PAIRSET_HH + +#include "PairValueRecord.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + + +template +struct PairSet +{ + template + friend struct PairPosFormat1_3; + + using PairValueRecord = GPOS_impl::PairValueRecord; + + protected: + HBUINT16 len; /* Number of PairValueRecords */ + PairValueRecord firstPairValueRecord; + /* Array of PairValueRecords--ordered + * by GlyphID of the second glyph */ + public: + DEFINE_SIZE_MIN (2); + + static unsigned get_size (unsigned len1, unsigned len2) + { + return Types::HBGlyphID::static_size + Value::static_size * (len1 + len2); + } + static unsigned get_size (const ValueFormat valueFormats[2]) + { + unsigned len1 = valueFormats[0].get_len (); + unsigned len2 = valueFormats[1].get_len (); + return get_size (len1, len2); + } + + struct sanitize_closure_t + { + const ValueFormat *valueFormats; + unsigned int len1; /* valueFormats[0].get_len() */ + unsigned int stride; /* bytes */ + }; + + bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) const + { + TRACE_SANITIZE (this); + if (!(c->check_struct (this) + && c->check_range (&firstPairValueRecord, + len, + closure->stride))) return_trace (false); + + unsigned int count = len; + const PairValueRecord *record = &firstPairValueRecord; + return_trace (c->lazy_some_gpos || + (closure->valueFormats[0].sanitize_values_stride_unsafe (c, this, &record->values[0], count, closure->stride) && + closure->valueFormats[1].sanitize_values_stride_unsafe (c, this, &record->values[closure->len1], count, closure->stride))); + } + + bool intersects (const hb_set_t *glyphs, + const ValueFormat *valueFormats) const + { + unsigned record_size = get_size (valueFormats); + + const PairValueRecord *record = &firstPairValueRecord; + unsigned int count = len; + for (unsigned int i = 0; i < count; i++) + { + if (glyphs->has (record->secondGlyph)) + return true; + record = &StructAtOffset (record, record_size); + } + return false; + } + + void collect_glyphs (hb_collect_glyphs_context_t *c, + const ValueFormat *valueFormats) const + { + unsigned record_size = get_size (valueFormats); + + const PairValueRecord *record = &firstPairValueRecord; + c->input->add_array (&record->secondGlyph, len, record_size); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c, + const ValueFormat *valueFormats) const + { + unsigned record_size = get_size (valueFormats); + + const PairValueRecord *record = &firstPairValueRecord; + unsigned count = len; + for (unsigned i = 0; i < count; i++) + { + if (c->glyph_set->has (record->secondGlyph)) + { record->collect_variation_indices (c, valueFormats, this); } + + record = &StructAtOffset (record, record_size); + } + } + + bool apply (hb_ot_apply_context_t *c, + const ValueFormat *valueFormats, + unsigned int pos) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + unsigned int len1 = valueFormats[0].get_len (); + unsigned int len2 = valueFormats[1].get_len (); + unsigned record_size = get_size (len1, len2); + + const PairValueRecord *record = hb_bsearch (buffer->info[pos].codepoint, + &firstPairValueRecord, + len, + record_size); + if (record) + { + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "try kerning glyphs at %u,%u", + c->buffer->idx, pos); + } + + bool applied_first = len1 && valueFormats[0].apply_value (c, this, &record->values[0], buffer->cur_pos()); + bool applied_second = len2 && valueFormats[1].apply_value (c, this, &record->values[len1], buffer->pos[pos]); + + if (applied_first || applied_second) + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "kerned glyphs at %u,%u", + c->buffer->idx, pos); + } + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "tried kerning glyphs at %u,%u", + c->buffer->idx, pos); + } + + if (applied_first || applied_second) + buffer->unsafe_to_break (buffer->idx, pos + 1); + + if (len2) + { + pos++; + // https://github.com/harfbuzz/harfbuzz/issues/3824 + // https://github.com/harfbuzz/harfbuzz/issues/3888#issuecomment-1326781116 + buffer->unsafe_to_break (buffer->idx, pos + 1); + } + + buffer->idx = pos; + return_trace (true); + } + buffer->unsafe_to_concat (buffer->idx, pos + 1); + return_trace (false); + } + + bool subset (hb_subset_context_t *c, + const ValueFormat valueFormats[2], + const ValueFormat newFormats[2]) const + { + TRACE_SUBSET (this); + auto snap = c->serializer->snapshot (); + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->len = 0; + + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + unsigned len1 = valueFormats[0].get_len (); + unsigned len2 = valueFormats[1].get_len (); + unsigned record_size = get_size (len1, len2); + + typename PairValueRecord::context_t context = + { + this, + valueFormats, + newFormats, + len1, + &glyph_map, + &c->plan->layout_variation_idx_delta_map + }; + + const PairValueRecord *record = &firstPairValueRecord; + unsigned count = len, num = 0; + for (unsigned i = 0; i < count; i++) + { + if (glyphset.has (record->secondGlyph) + && record->subset (c, &context)) num++; + record = &StructAtOffset (record, record_size); + } + + out->len = num; + if (!num) c->serializer->revert (snap); + return_trace (num); + } +}; + + +} +} +} + +#endif // OT_LAYOUT_GPOS_PAIRSET_HH diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PairValueRecord.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PairValueRecord.hh new file mode 100644 index 00000000000..72bf0e99b53 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PairValueRecord.hh @@ -0,0 +1,99 @@ +#ifndef OT_LAYOUT_GPOS_PAIRVALUERECORD_HH +#define OT_LAYOUT_GPOS_PAIRVALUERECORD_HH + +#include "ValueFormat.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + + +template +struct PairValueRecord +{ + template + friend struct PairSet; + + protected: + typename Types::HBGlyphID + secondGlyph; /* GlyphID of second glyph in the + * pair--first glyph is listed in the + * Coverage table */ + ValueRecord values; /* Positioning data for the first glyph + * followed by for second glyph */ + public: + DEFINE_SIZE_ARRAY (Types::HBGlyphID::static_size, values); + + int cmp (hb_codepoint_t k) const + { return secondGlyph.cmp (k); } + + struct context_t + { + const void *base; + const ValueFormat *valueFormats; + const ValueFormat *newFormats; + unsigned len1; /* valueFormats[0].get_len() */ + const hb_map_t *glyph_map; + const hb_hashmap_t> *layout_variation_idx_delta_map; + }; + + bool subset (hb_subset_context_t *c, + context_t *closure) const + { + TRACE_SERIALIZE (this); + auto *s = c->serializer; + auto *out = s->start_embed (*this); + if (unlikely (!s->extend_min (out))) return_trace (false); + + out->secondGlyph = (*closure->glyph_map)[secondGlyph]; + + closure->valueFormats[0].copy_values (s, + closure->newFormats[0], + closure->base, &values[0], + closure->layout_variation_idx_delta_map); + closure->valueFormats[1].copy_values (s, + closure->newFormats[1], + closure->base, + &values[closure->len1], + closure->layout_variation_idx_delta_map); + + return_trace (true); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c, + const ValueFormat *valueFormats, + const void *base) const + { + unsigned record1_len = valueFormats[0].get_len (); + unsigned record2_len = valueFormats[1].get_len (); + const hb_array_t values_array = values.as_array (record1_len + record2_len); + + if (valueFormats[0].has_device ()) + valueFormats[0].collect_variation_indices (c, base, values_array.sub_array (0, record1_len)); + + if (valueFormats[1].has_device ()) + valueFormats[1].collect_variation_indices (c, base, values_array.sub_array (record1_len, record2_len)); + } + + bool intersects (const hb_set_t& glyphset) const + { + return glyphset.has(secondGlyph); + } + + const Value* get_values_1 () const + { + return &values[0]; + } + + const Value* get_values_2 (ValueFormat format1) const + { + return &values[format1.get_len ()]; + } +}; + + +} +} +} + +#endif // OT_LAYOUT_GPOS_PAIRVALUERECORD_HH diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PosLookup.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PosLookup.hh new file mode 100644 index 00000000000..c4e57bb5430 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PosLookup.hh @@ -0,0 +1,79 @@ +#ifndef OT_LAYOUT_GPOS_POSLOOKUP_HH +#define OT_LAYOUT_GPOS_POSLOOKUP_HH + +#include "PosLookupSubTable.hh" +#include "../../../hb-ot-layout-common.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct PosLookup : Lookup +{ + using SubTable = PosLookupSubTable; + + const SubTable& get_subtable (unsigned int i) const + { return Lookup::get_subtable (i); } + + bool is_reverse () const + { + return false; + } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + return_trace (dispatch (c)); + } + + bool intersects (const hb_set_t *glyphs) const + { + hb_intersects_context_t c (glyphs); + return dispatch (&c); + } + + hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const + { return dispatch (c); } + + hb_closure_lookups_context_t::return_t closure_lookups (hb_closure_lookups_context_t *c, unsigned this_index) const + { + if (c->is_lookup_visited (this_index)) + return hb_closure_lookups_context_t::default_return_value (); + + c->set_lookup_visited (this_index); + if (!intersects (c->glyphs)) + { + c->set_lookup_inactive (this_index); + return hb_closure_lookups_context_t::default_return_value (); + } + + hb_closure_lookups_context_t::return_t ret = dispatch (c); + return ret; + } + + template + void collect_coverage (set_t *glyphs) const + { + hb_collect_coverage_context_t c (glyphs); + dispatch (&c); + } + + template + static typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index); + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { return Lookup::dispatch (c, std::forward (ds)...); } + + bool subset (hb_subset_context_t *c) const + { return Lookup::subset (c); } + + bool sanitize (hb_sanitize_context_t *c) const + { return Lookup::sanitize (c); } +}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_POSLOOKUP_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PosLookupSubTable.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PosLookupSubTable.hh new file mode 100644 index 00000000000..c19fbc323ff --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/PosLookupSubTable.hh @@ -0,0 +1,79 @@ +#ifndef OT_LAYOUT_GPOS_POSLOOKUPSUBTABLE_HH +#define OT_LAYOUT_GPOS_POSLOOKUPSUBTABLE_HH + +#include "SinglePos.hh" +#include "PairPos.hh" +#include "CursivePos.hh" +#include "MarkBasePos.hh" +#include "MarkLigPos.hh" +#include "MarkMarkPos.hh" +#include "ContextPos.hh" +#include "ChainContextPos.hh" +#include "ExtensionPos.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct PosLookupSubTable +{ + friend struct ::OT::Lookup; + friend struct PosLookup; + + enum Type { + Single = 1, + Pair = 2, + Cursive = 3, + MarkBase = 4, + MarkLig = 5, + MarkMark = 6, + Context = 7, + ChainContext = 8, + Extension = 9 + }; + + template + typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type, Ts&&... ds) const + { + TRACE_DISPATCH (this, lookup_type); + switch (lookup_type) { + case Single: return_trace (u.single.dispatch (c, std::forward (ds)...)); + case Pair: return_trace (u.pair.dispatch (c, std::forward (ds)...)); + case Cursive: return_trace (u.cursive.dispatch (c, std::forward (ds)...)); + case MarkBase: return_trace (u.markBase.dispatch (c, std::forward (ds)...)); + case MarkLig: return_trace (u.markLig.dispatch (c, std::forward (ds)...)); + case MarkMark: return_trace (u.markMark.dispatch (c, std::forward (ds)...)); + case Context: return_trace (u.context.dispatch (c, std::forward (ds)...)); + case ChainContext: return_trace (u.chainContext.dispatch (c, std::forward (ds)...)); + case Extension: return_trace (u.extension.dispatch (c, std::forward (ds)...)); + default: return_trace (c->default_return_value ()); + } + } + + bool intersects (const hb_set_t *glyphs, unsigned int lookup_type) const + { + hb_intersects_context_t c (glyphs); + return dispatch (&c, lookup_type); + } + + protected: + union { + SinglePos single; + PairPos pair; + CursivePos cursive; + MarkBasePos markBase; + MarkLigPos markLig; + MarkMarkPos markMark; + ContextPos context; + ChainContextPos chainContext; + ExtensionPos extension; + } u; + public: + DEFINE_SIZE_MIN (0); +}; + +} +} +} + +#endif /* HB_OT_LAYOUT_GPOS_POSLOOKUPSUBTABLE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/SinglePos.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/SinglePos.hh new file mode 100644 index 00000000000..3af6c499659 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/SinglePos.hh @@ -0,0 +1,100 @@ +#ifndef OT_LAYOUT_GPOS_SINGLEPOS_HH +#define OT_LAYOUT_GPOS_SINGLEPOS_HH + +#include "SinglePosFormat1.hh" +#include "SinglePosFormat2.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct SinglePos +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + SinglePosFormat1 format1; + SinglePosFormat2 format2; + } u; + + public: + template + unsigned get_format (Iterator glyph_val_iter_pairs) + { + hb_array_t first_val_iter = hb_second (*glyph_val_iter_pairs); + + for (const auto iter : glyph_val_iter_pairs) + for (const auto _ : hb_zip (iter.second, first_val_iter)) + if (_.first != _.second) + return 2; + + return 1; + } + + template + void serialize (hb_serialize_context_t *c, + const SrcLookup* src, + Iterator glyph_val_iter_pairs, + const hb_hashmap_t> *layout_variation_idx_delta_map, + bool all_axes_pinned) + { + if (unlikely (!c->extend_min (u.format))) return; + unsigned format = 2; + ValueFormat new_format = src->get_value_format (); + + if (all_axes_pinned) + new_format = new_format.drop_device_table_flags (); + + if (glyph_val_iter_pairs) + format = get_format (glyph_val_iter_pairs); + + u.format = format; + switch (u.format) { + case 1: u.format1.serialize (c, + src, + glyph_val_iter_pairs, + new_format, + layout_variation_idx_delta_map); + return; + case 2: u.format2.serialize (c, + src, + glyph_val_iter_pairs, + new_format, + layout_variation_idx_delta_map); + return; + default:return; + } + } + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); + default:return_trace (c->default_return_value ()); + } + } +}; + + +template +static void +SinglePos_serialize (hb_serialize_context_t *c, + const SrcLookup *src, + Iterator it, + const hb_hashmap_t> *layout_variation_idx_delta_map, + bool all_axes_pinned) +{ c->start_embed ()->serialize (c, src, it, layout_variation_idx_delta_map, all_axes_pinned); } + + +} +} +} + +#endif /* OT_LAYOUT_GPOS_SINGLEPOS_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/SinglePosFormat1.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/SinglePosFormat1.hh new file mode 100644 index 00000000000..dff1f7316d5 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/SinglePosFormat1.hh @@ -0,0 +1,165 @@ +#ifndef OT_LAYOUT_GPOS_SINGLEPOSFORMAT1_HH +#define OT_LAYOUT_GPOS_SINGLEPOSFORMAT1_HH + +#include "Common.hh" +#include "ValueFormat.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct SinglePosFormat1 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + Offset16To + coverage; /* Offset to Coverage table--from + * beginning of subtable */ + ValueFormat valueFormat; /* Defines the types of data in the + * ValueRecord */ + ValueRecord values; /* Defines positioning + * value(s)--applied to all glyphs in + * the Coverage table */ + public: + DEFINE_SIZE_ARRAY (6, values); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + coverage.sanitize (c, this) && + /* The coverage table may use a range to represent a set + * of glyphs, which means a small number of bytes can + * generate a large glyph set. Manually modify the + * sanitizer max ops to take this into account. + * + * Note: This check *must* be right after coverage sanitize. */ + c->check_ops ((this + coverage).get_population () >> 1) && + valueFormat.sanitize_value (c, this, values)); + + } + + bool intersects (const hb_set_t *glyphs) const + { return (this+coverage).intersects (glyphs); } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + if (!valueFormat.has_device ()) return; + + hb_set_t intersection; + (this+coverage).intersect_set (*c->glyph_set, intersection); + if (!intersection) return; + + valueFormat.collect_variation_indices (c, this, values.as_array (valueFormat.get_len ())); + } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { if (unlikely (!(this+coverage).collect_coverage (c->input))) return; } + + const Coverage &get_coverage () const { return this+coverage; } + + ValueFormat get_value_format () const { return valueFormat; } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "positioning glyph at %u", + c->buffer->idx); + } + + valueFormat.apply_value (c, this, values, buffer->cur_pos()); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "positioned glyph at %u", + c->buffer->idx); + } + + buffer->idx++; + return_trace (true); + } + + bool + position_single (hb_font_t *font, + hb_blob_t *table_blob, + hb_direction_t direction, + hb_codepoint_t gid, + hb_glyph_position_t &pos) const + { + unsigned int index = (this+coverage).get_coverage (gid); + if (likely (index == NOT_COVERED)) return false; + + /* This is ugly... */ + hb_buffer_t buffer; + buffer.props.direction = direction; + OT::hb_ot_apply_context_t c (1, font, &buffer, table_blob); + + valueFormat.apply_value (&c, this, values, pos); + return true; + } + + template + void serialize (hb_serialize_context_t *c, + const SrcLookup *src, + Iterator it, + ValueFormat newFormat, + const hb_hashmap_t> *layout_variation_idx_delta_map) + { + if (unlikely (!c->extend_min (this))) return; + if (unlikely (!c->check_assign (valueFormat, + newFormat, + HB_SERIALIZE_ERROR_INT_OVERFLOW))) return; + + for (const hb_array_t& _ : + it | hb_map (hb_second)) + { + src->get_value_format ().copy_values (c, newFormat, src, &_, layout_variation_idx_delta_map); + // Only serialize the first entry in the iterator, the rest are assumed to + // be the same. + break; + } + + auto glyphs = + + it + | hb_map_retains_sorting (hb_first) + ; + + coverage.serialize_serialize (c, glyphs); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + hb_set_t intersection; + (this+coverage).intersect_set (glyphset, intersection); + + auto it = + + hb_iter (intersection) + | hb_map_retains_sorting (glyph_map) + | hb_zip (hb_repeat (values.as_array (valueFormat.get_len ()))) + ; + + bool ret = bool (it); + SinglePos_serialize (c->serializer, this, it, &c->plan->layout_variation_idx_delta_map, c->plan->all_axes_pinned); + return_trace (ret); + } +}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_SINGLEPOSFORMAT1_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/SinglePosFormat2.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/SinglePosFormat2.hh new file mode 100644 index 00000000000..168ad3bb8c8 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/SinglePosFormat2.hh @@ -0,0 +1,177 @@ +#ifndef OT_LAYOUT_GPOS_SINGLEPOSFORMAT2_HH +#define OT_LAYOUT_GPOS_SINGLEPOSFORMAT2_HH + +#include "Common.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct SinglePosFormat2 +{ + protected: + HBUINT16 format; /* Format identifier--format = 2 */ + Offset16To + coverage; /* Offset to Coverage table--from + * beginning of subtable */ + ValueFormat valueFormat; /* Defines the types of data in the + * ValueRecord */ + HBUINT16 valueCount; /* Number of ValueRecords */ + ValueRecord values; /* Array of ValueRecords--positioning + * values applied to glyphs */ + public: + DEFINE_SIZE_ARRAY (8, values); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + coverage.sanitize (c, this) && + valueFormat.sanitize_values (c, this, values, valueCount)); + } + + bool intersects (const hb_set_t *glyphs) const + { return (this+coverage).intersects (glyphs); } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + if (!valueFormat.has_device ()) return; + + auto it = + + hb_zip (this+coverage, hb_range ((unsigned) valueCount)) + | hb_filter (c->glyph_set, hb_first) + ; + + if (!it) return; + + unsigned sub_length = valueFormat.get_len (); + const hb_array_t values_array = values.as_array (valueCount * sub_length); + + for (unsigned i : + it + | hb_map (hb_second)) + valueFormat.collect_variation_indices (c, this, values_array.sub_array (i * sub_length, sub_length)); + + } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { if (unlikely (!(this+coverage).collect_coverage (c->input))) return; } + + const Coverage &get_coverage () const { return this+coverage; } + + ValueFormat get_value_format () const { return valueFormat; } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + if (unlikely (index >= valueCount)) return_trace (false); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "positioning glyph at %u", + c->buffer->idx); + } + + valueFormat.apply_value (c, this, + &values[index * valueFormat.get_len ()], + buffer->cur_pos()); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "positioned glyph at %u", + c->buffer->idx); + } + + buffer->idx++; + return_trace (true); + } + + bool + position_single (hb_font_t *font, + hb_blob_t *table_blob, + hb_direction_t direction, + hb_codepoint_t gid, + hb_glyph_position_t &pos) const + { + unsigned int index = (this+coverage).get_coverage (gid); + if (likely (index == NOT_COVERED)) return false; + if (unlikely (index >= valueCount)) return false; + + /* This is ugly... */ + hb_buffer_t buffer; + buffer.props.direction = direction; + OT::hb_ot_apply_context_t c (1, font, &buffer, table_blob); + + valueFormat.apply_value (&c, this, + &values[index * valueFormat.get_len ()], + pos); + return true; + } + + + template + void serialize (hb_serialize_context_t *c, + const SrcLookup *src, + Iterator it, + ValueFormat newFormat, + const hb_hashmap_t> *layout_variation_idx_delta_map) + { + auto out = c->extend_min (this); + if (unlikely (!out)) return; + if (unlikely (!c->check_assign (valueFormat, newFormat, HB_SERIALIZE_ERROR_INT_OVERFLOW))) return; + if (unlikely (!c->check_assign (valueCount, it.len (), HB_SERIALIZE_ERROR_ARRAY_OVERFLOW))) return; + + + it + | hb_map (hb_second) + | hb_apply ([&] (hb_array_t _) + { src->get_value_format ().copy_values (c, newFormat, src, &_, layout_variation_idx_delta_map); }) + ; + + auto glyphs = + + it + | hb_map_retains_sorting (hb_first) + ; + + coverage.serialize_serialize (c, glyphs); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + unsigned sub_length = valueFormat.get_len (); + auto values_array = values.as_array (valueCount * sub_length); + + auto it = + + hb_zip (this+coverage, hb_range ((unsigned) valueCount)) + | hb_filter (glyphset, hb_first) + | hb_map_retains_sorting ([&] (const hb_pair_t& _) + { + return hb_pair (glyph_map[_.first], + values_array.sub_array (_.second * sub_length, + sub_length)); + }) + ; + + bool ret = bool (it); + SinglePos_serialize (c->serializer, this, it, &c->plan->layout_variation_idx_delta_map, c->plan->all_axes_pinned); + return_trace (ret); + } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GPOS_SINGLEPOSFORMAT2_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/ValueFormat.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/ValueFormat.hh new file mode 100644 index 00000000000..461a13d4b70 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GPOS/ValueFormat.hh @@ -0,0 +1,412 @@ +#ifndef OT_LAYOUT_GPOS_VALUEFORMAT_HH +#define OT_LAYOUT_GPOS_VALUEFORMAT_HH + +#include "../../../hb-ot-layout-gsubgpos.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +typedef HBUINT16 Value; + +typedef UnsizedArrayOf ValueRecord; + +struct ValueFormat : HBUINT16 +{ + enum Flags { + xPlacement = 0x0001u, /* Includes horizontal adjustment for placement */ + yPlacement = 0x0002u, /* Includes vertical adjustment for placement */ + xAdvance = 0x0004u, /* Includes horizontal adjustment for advance */ + yAdvance = 0x0008u, /* Includes vertical adjustment for advance */ + xPlaDevice = 0x0010u, /* Includes horizontal Device table for placement */ + yPlaDevice = 0x0020u, /* Includes vertical Device table for placement */ + xAdvDevice = 0x0040u, /* Includes horizontal Device table for advance */ + yAdvDevice = 0x0080u, /* Includes vertical Device table for advance */ + ignored = 0x0F00u, /* Was used in TrueType Open for MM fonts */ + reserved = 0xF000u, /* For future use */ + + devices = 0x00F0u /* Mask for having any Device table */ + }; + +/* All fields are options. Only those available advance the value pointer. */ +#if 0 + HBINT16 xPlacement; /* Horizontal adjustment for + * placement--in design units */ + HBINT16 yPlacement; /* Vertical adjustment for + * placement--in design units */ + HBINT16 xAdvance; /* Horizontal adjustment for + * advance--in design units (only used + * for horizontal writing) */ + HBINT16 yAdvance; /* Vertical adjustment for advance--in + * design units (only used for vertical + * writing) */ + Offset16To xPlaDevice; /* Offset to Device table for + * horizontal placement--measured from + * beginning of PosTable (may be NULL) */ + Offset16To yPlaDevice; /* Offset to Device table for vertical + * placement--measured from beginning + * of PosTable (may be NULL) */ + Offset16To xAdvDevice; /* Offset to Device table for + * horizontal advance--measured from + * beginning of PosTable (may be NULL) */ + Offset16To yAdvDevice; /* Offset to Device table for vertical + * advance--measured from beginning of + * PosTable (may be NULL) */ +#endif + + IntType& operator = (uint16_t i) { v = i; return *this; } + + unsigned int get_len () const { return hb_popcount ((unsigned int) *this); } + unsigned int get_size () const { return get_len () * Value::static_size; } + + hb_vector_t get_device_table_indices () const { + unsigned i = 0; + hb_vector_t result; + unsigned format = *this; + + if (format & xPlacement) i++; + if (format & yPlacement) i++; + if (format & xAdvance) i++; + if (format & yAdvance) i++; + + if (format & xPlaDevice) result.push (i++); + if (format & yPlaDevice) result.push (i++); + if (format & xAdvDevice) result.push (i++); + if (format & yAdvDevice) result.push (i++); + + return result; + } + + bool apply_value (hb_ot_apply_context_t *c, + const void *base, + const Value *values, + hb_glyph_position_t &glyph_pos) const + { + bool ret = false; + unsigned int format = *this; + if (!format) return ret; + + hb_font_t *font = c->font; + bool horizontal = +#ifndef HB_NO_VERTICAL + HB_DIRECTION_IS_HORIZONTAL (c->direction) +#else + true +#endif + ; + + if (format & xPlacement) glyph_pos.x_offset += font->em_scale_x (get_short (values++, &ret)); + if (format & yPlacement) glyph_pos.y_offset += font->em_scale_y (get_short (values++, &ret)); + if (format & xAdvance) { + if (likely (horizontal)) glyph_pos.x_advance += font->em_scale_x (get_short (values, &ret)); + values++; + } + /* y_advance values grow downward but font-space grows upward, hence negation */ + if (format & yAdvance) { + if (unlikely (!horizontal)) glyph_pos.y_advance -= font->em_scale_y (get_short (values, &ret)); + values++; + } + + if (!has_device ()) return ret; + + bool use_x_device = font->x_ppem || font->num_coords; + bool use_y_device = font->y_ppem || font->num_coords; + + if (!use_x_device && !use_y_device) return ret; + + const VariationStore &store = c->var_store; + auto *cache = c->var_store_cache; + + /* pixel -> fractional pixel */ + if (format & xPlaDevice) + { + if (use_x_device) glyph_pos.x_offset += get_device (values, &ret, base, c->sanitizer).get_x_delta (font, store, cache); + values++; + } + if (format & yPlaDevice) + { + if (use_y_device) glyph_pos.y_offset += get_device (values, &ret, base, c->sanitizer).get_y_delta (font, store, cache); + values++; + } + if (format & xAdvDevice) + { + if (horizontal && use_x_device) glyph_pos.x_advance += get_device (values, &ret, base, c->sanitizer).get_x_delta (font, store, cache); + values++; + } + if (format & yAdvDevice) + { + /* y_advance values grow downward but font-space grows upward, hence negation */ + if (!horizontal && use_y_device) glyph_pos.y_advance -= get_device (values, &ret, base, c->sanitizer).get_y_delta (font, store, cache); + values++; + } + return ret; + } + + unsigned int get_effective_format (const Value *values) const + { + unsigned int format = *this; + for (unsigned flag = xPlacement; flag <= yAdvDevice; flag = flag << 1) { + if (format & flag) should_drop (*values++, (Flags) flag, &format); + } + + return format; + } + + template + unsigned int get_effective_format (Iterator it) const { + unsigned int new_format = 0; + + for (const hb_array_t& values : it) + new_format = new_format | get_effective_format (&values); + + return new_format; + } + + void copy_values (hb_serialize_context_t *c, + unsigned int new_format, + const void *base, + const Value *values, + const hb_hashmap_t> *layout_variation_idx_delta_map) const + { + unsigned int format = *this; + if (!format) return; + + HBINT16 *x_placement = nullptr, *y_placement = nullptr, *x_adv = nullptr, *y_adv = nullptr; + if (format & xPlacement) x_placement = copy_value (c, new_format, xPlacement, *values++); + if (format & yPlacement) y_placement = copy_value (c, new_format, yPlacement, *values++); + if (format & xAdvance) x_adv = copy_value (c, new_format, xAdvance, *values++); + if (format & yAdvance) y_adv = copy_value (c, new_format, yAdvance, *values++); + + if (!has_device ()) + return; + + if (format & xPlaDevice) + { + add_delta_to_value (x_placement, base, values, layout_variation_idx_delta_map); + copy_device (c, base, values++, layout_variation_idx_delta_map, new_format, xPlaDevice); + } + + if (format & yPlaDevice) + { + add_delta_to_value (y_placement, base, values, layout_variation_idx_delta_map); + copy_device (c, base, values++, layout_variation_idx_delta_map, new_format, yPlaDevice); + } + + if (format & xAdvDevice) + { + add_delta_to_value (x_adv, base, values, layout_variation_idx_delta_map); + copy_device (c, base, values++, layout_variation_idx_delta_map, new_format, xAdvDevice); + } + + if (format & yAdvDevice) + { + add_delta_to_value (y_adv, base, values, layout_variation_idx_delta_map); + copy_device (c, base, values++, layout_variation_idx_delta_map, new_format, yAdvDevice); + } + } + + HBINT16* copy_value (hb_serialize_context_t *c, + unsigned int new_format, + Flags flag, + Value value) const + { + // Filter by new format. + if (!(new_format & flag)) return nullptr; + return reinterpret_cast (c->copy (value)); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c, + const void *base, + const hb_array_t& values) const + { + unsigned format = *this; + unsigned i = 0; + if (format & xPlacement) i++; + if (format & yPlacement) i++; + if (format & xAdvance) i++; + if (format & yAdvance) i++; + if (format & xPlaDevice) + { + (base + get_device (&(values[i]))).collect_variation_indices (c); + i++; + } + + if (format & ValueFormat::yPlaDevice) + { + (base + get_device (&(values[i]))).collect_variation_indices (c); + i++; + } + + if (format & ValueFormat::xAdvDevice) + { + (base + get_device (&(values[i]))).collect_variation_indices (c); + i++; + } + + if (format & ValueFormat::yAdvDevice) + { + (base + get_device (&(values[i]))).collect_variation_indices (c); + i++; + } + } + + unsigned drop_device_table_flags () const + { + unsigned format = *this; + for (unsigned flag = xPlaDevice; flag <= yAdvDevice; flag = flag << 1) + format = format & ~flag; + + return format; + } + + private: + bool sanitize_value_devices (hb_sanitize_context_t *c, const void *base, const Value *values) const + { + unsigned int format = *this; + + if (format & xPlacement) values++; + if (format & yPlacement) values++; + if (format & xAdvance) values++; + if (format & yAdvance) values++; + + if ((format & xPlaDevice) && !get_device (values++).sanitize (c, base)) return false; + if ((format & yPlaDevice) && !get_device (values++).sanitize (c, base)) return false; + if ((format & xAdvDevice) && !get_device (values++).sanitize (c, base)) return false; + if ((format & yAdvDevice) && !get_device (values++).sanitize (c, base)) return false; + + return true; + } + + static inline Offset16To& get_device (Value* value) + { + return *static_cast *> (value); + } + static inline const Offset16To& get_device (const Value* value) + { + return *static_cast *> (value); + } + static inline const Device& get_device (const Value* value, + bool *worked, + const void *base, + hb_sanitize_context_t &c) + { + if (worked) *worked |= bool (*value); + auto &offset = *static_cast *> (value); + + if (unlikely (!offset.sanitize (&c, base))) + return Null(Device); + + return base + offset; + } + + void add_delta_to_value (HBINT16 *value, + const void *base, + const Value *src_value, + const hb_hashmap_t> *layout_variation_idx_delta_map) const + { + if (!value) return; + unsigned varidx = (base + get_device (src_value)).get_variation_index (); + hb_pair_t *varidx_delta; + if (!layout_variation_idx_delta_map->has (varidx, &varidx_delta)) return; + + *value += hb_second (*varidx_delta); + } + + bool copy_device (hb_serialize_context_t *c, const void *base, + const Value *src_value, + const hb_hashmap_t> *layout_variation_idx_delta_map, + unsigned int new_format, Flags flag) const + { + // Filter by new format. + if (!(new_format & flag)) return true; + + Value *dst_value = c->copy (*src_value); + + if (!dst_value) return false; + if (*dst_value == 0) return true; + + *dst_value = 0; + c->push (); + if ((base + get_device (src_value)).copy (c, layout_variation_idx_delta_map)) + { + c->add_link (*dst_value, c->pop_pack ()); + return true; + } + else + { + c->pop_discard (); + return false; + } + } + + static inline const HBINT16& get_short (const Value* value, bool *worked=nullptr) + { + if (worked) *worked |= bool (*value); + return *reinterpret_cast (value); + } + + public: + + bool has_device () const + { + unsigned int format = *this; + return (format & devices) != 0; + } + + bool sanitize_value (hb_sanitize_context_t *c, const void *base, const Value *values) const + { + TRACE_SANITIZE (this); + + if (unlikely (!c->check_range (values, get_size ()))) return_trace (false); + + if (c->lazy_some_gpos) + return_trace (true); + + return_trace (!has_device () || sanitize_value_devices (c, base, values)); + } + + bool sanitize_values (hb_sanitize_context_t *c, const void *base, const Value *values, unsigned int count) const + { + TRACE_SANITIZE (this); + unsigned size = get_size (); + + if (!c->check_range (values, count, size)) return_trace (false); + + if (c->lazy_some_gpos) + return_trace (true); + + return_trace (sanitize_values_stride_unsafe (c, base, values, count, size)); + } + + /* Just sanitize referenced Device tables. Doesn't check the values themselves. */ + bool sanitize_values_stride_unsafe (hb_sanitize_context_t *c, const void *base, const Value *values, unsigned int count, unsigned int stride) const + { + TRACE_SANITIZE (this); + + if (!has_device ()) return_trace (true); + + for (unsigned int i = 0; i < count; i++) { + if (!sanitize_value_devices (c, base, values)) + return_trace (false); + values = &StructAtOffset (values, stride); + } + + return_trace (true); + } + + private: + + void should_drop (Value value, Flags flag, unsigned int* format) const + { + if (value) return; + *format = *format & ~flag; + } + +}; + +} +} +} + +#endif // #ifndef OT_LAYOUT_GPOS_VALUEFORMAT_HH diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/AlternateSet.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/AlternateSet.hh new file mode 100644 index 00000000000..b4466119be0 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/AlternateSet.hh @@ -0,0 +1,126 @@ +#ifndef OT_LAYOUT_GSUB_ALTERNATESET_HH +#define OT_LAYOUT_GSUB_ALTERNATESET_HH + +#include "Common.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +template +struct AlternateSet +{ + protected: + Array16Of + alternates; /* Array of alternate GlyphIDs--in + * arbitrary order */ + public: + DEFINE_SIZE_ARRAY (2, alternates); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (alternates.sanitize (c)); + } + + bool intersects (const hb_set_t *glyphs) const + { return hb_any (alternates, glyphs); } + + void closure (hb_closure_context_t *c) const + { c->output->add_array (alternates.arrayZ, alternates.len); } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { c->output->add_array (alternates.arrayZ, alternates.len); } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + unsigned int count = alternates.len; + + if (unlikely (!count)) return_trace (false); + + hb_mask_t glyph_mask = c->buffer->cur().mask; + hb_mask_t lookup_mask = c->lookup_mask; + + /* Note: This breaks badly if two features enabled this lookup together. */ + unsigned int shift = hb_ctz (lookup_mask); + unsigned int alt_index = ((lookup_mask & glyph_mask) >> shift); + + /* If alt_index is MAX_VALUE, randomize feature if it is the rand feature. */ + if (alt_index == HB_OT_MAP_MAX_VALUE && c->random) + { + /* Maybe we can do better than unsafe-to-break all; but since we are + * changing random state, it would be hard to track that. Good 'nough. */ + c->buffer->unsafe_to_break (0, c->buffer->len); + alt_index = c->random_number () % count + 1; + } + + if (unlikely (alt_index > count || alt_index == 0)) return_trace (false); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->sync_so_far (); + c->buffer->message (c->font, + "replacing glyph at %u (alternate substitution)", + c->buffer->idx); + } + + c->replace_glyph (alternates[alt_index - 1]); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "replaced glyph at %u (alternate substitution)", + c->buffer->idx - 1u); + } + + return_trace (true); + } + + unsigned + get_alternates (unsigned start_offset, + unsigned *alternate_count /* IN/OUT. May be NULL. */, + hb_codepoint_t *alternate_glyphs /* OUT. May be NULL. */) const + { + if (alternates.len && alternate_count) + { + + alternates.as_array ().sub_array (start_offset, alternate_count) + | hb_sink (hb_array (alternate_glyphs, *alternate_count)) + ; + } + return alternates.len; + } + + template + bool serialize (hb_serialize_context_t *c, + Iterator alts) + { + TRACE_SERIALIZE (this); + return_trace (alternates.serialize (c, alts)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto it = + + hb_iter (alternates) + | hb_filter (glyphset) + | hb_map (glyph_map) + ; + + auto *out = c->serializer->start_embed (*this); + return_trace (out->serialize (c->serializer, it) && + out->alternates); + } +}; + +} +} +} + + +#endif /* OT_LAYOUT_GSUB_ALTERNATESET_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/AlternateSubst.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/AlternateSubst.hh new file mode 100644 index 00000000000..04a052a7836 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/AlternateSubst.hh @@ -0,0 +1,62 @@ +#ifndef OT_LAYOUT_GSUB_ALTERNATESUBST_HH +#define OT_LAYOUT_GSUB_ALTERNATESUBST_HH + +#include "AlternateSubstFormat1.hh" +#include "Common.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +struct AlternateSubst +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + AlternateSubstFormat1_2 format1; +#ifndef HB_NO_BEYOND_64K + AlternateSubstFormat1_2 format2; +#endif + } u; + public: + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); +#ifndef HB_NO_BEYOND_64K + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); +#endif + default:return_trace (c->default_return_value ()); + } + } + + /* TODO This function is unused and not updated to 24bit GIDs. Should be done by using + * iterators. While at it perhaps using iterator of arrays of hb_codepoint_t instead. */ + bool serialize (hb_serialize_context_t *c, + hb_sorted_array_t glyphs, + hb_array_t alternate_len_list, + hb_array_t alternate_glyphs_list) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (u.format))) return_trace (false); + unsigned int format = 1; + u.format = format; + switch (u.format) { + case 1: return_trace (u.format1.serialize (c, glyphs, alternate_len_list, alternate_glyphs_list)); + default:return_trace (false); + } + } + + /* TODO subset() should choose format. */ + +}; + +} +} +} + +#endif /* OT_LAYOUT_GSUB_ALTERNATESUBST_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/AlternateSubstFormat1.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/AlternateSubstFormat1.hh new file mode 100644 index 00000000000..adec65d5864 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/AlternateSubstFormat1.hh @@ -0,0 +1,128 @@ +#ifndef OT_LAYOUT_GSUB_ALTERNATESUBSTFORMAT1_HH +#define OT_LAYOUT_GSUB_ALTERNATESUBSTFORMAT1_HH + +#include "AlternateSet.hh" +#include "Common.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +template +struct AlternateSubstFormat1_2 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + typename Types::template OffsetTo + coverage; /* Offset to Coverage table--from + * beginning of Substitution table */ + Array16Of>> + alternateSet; /* Array of AlternateSet tables + * ordered by Coverage Index */ + public: + DEFINE_SIZE_ARRAY (2 + 2 * Types::size, alternateSet); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (coverage.sanitize (c, this) && alternateSet.sanitize (c, this)); + } + + bool intersects (const hb_set_t *glyphs) const + { return (this+coverage).intersects (glyphs); } + + bool may_have_non_1to1 () const + { return false; } + + void closure (hb_closure_context_t *c) const + { + + hb_zip (this+coverage, alternateSet) + | hb_filter (c->parent_active_glyphs (), hb_first) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([c] (const AlternateSet &_) { _.closure (c); }) + ; + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; + + hb_zip (this+coverage, alternateSet) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([c] (const AlternateSet &_) { _.collect_glyphs (c); }) + ; + } + + const Coverage &get_coverage () const { return this+coverage; } + + bool would_apply (hb_would_apply_context_t *c) const + { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; } + + unsigned + get_glyph_alternates (hb_codepoint_t gid, + unsigned start_offset, + unsigned *alternate_count /* IN/OUT. May be NULL. */, + hb_codepoint_t *alternate_glyphs /* OUT. May be NULL. */) const + { return (this+alternateSet[(this+coverage).get_coverage (gid)]) + .get_alternates (start_offset, alternate_count, alternate_glyphs); } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + + unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + return_trace ((this+alternateSet[index]).apply (c)); + } + + bool serialize (hb_serialize_context_t *c, + hb_sorted_array_t glyphs, + hb_array_t alternate_len_list, + hb_array_t alternate_glyphs_list) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (this))) return_trace (false); + if (unlikely (!alternateSet.serialize (c, glyphs.length))) return_trace (false); + for (unsigned int i = 0; i < glyphs.length; i++) + { + unsigned int alternate_len = alternate_len_list[i]; + if (unlikely (!alternateSet[i] + .serialize_serialize (c, alternate_glyphs_list.sub_array (0, alternate_len)))) + return_trace (false); + alternate_glyphs_list += alternate_len; + } + return_trace (coverage.serialize_serialize (c, glyphs)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + hb_sorted_vector_t new_coverage; + + hb_zip (this+coverage, alternateSet) + | hb_filter (glyphset, hb_first) + | hb_filter (subset_offset_array (c, out->alternateSet, this), hb_second) + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + out->coverage.serialize_serialize (c->serializer, new_coverage.iter ()); + return_trace (bool (new_coverage)); + } +}; + +} +} +} + +#endif /* OT_LAYOUT_GSUB_ALTERNATESUBSTFORMAT1_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/ChainContextSubst.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/ChainContextSubst.hh new file mode 100644 index 00000000000..08fd779f730 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/ChainContextSubst.hh @@ -0,0 +1,18 @@ +#ifndef OT_LAYOUT_GSUB_CHAINCONTEXTSUBST_HH +#define OT_LAYOUT_GSUB_CHAINCONTEXTSUBST_HH + +// TODO(garretrieger): move to new layout. +#include "../../../hb-ot-layout-gsubgpos.hh" +#include "Common.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +struct ChainContextSubst : ChainContext {}; + +} +} +} + +#endif /* OT_LAYOUT_GSUB_CHAINCONTEXTSUBST_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/Common.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/Common.hh new file mode 100644 index 00000000000..b849494d88a --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/Common.hh @@ -0,0 +1,19 @@ +#ifndef OT_LAYOUT_GSUB_COMMON_HH +#define OT_LAYOUT_GSUB_COMMON_HH + +#include "../../../hb-serialize.hh" +#include "../../../hb-ot-layout-gsubgpos.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +template +static void SingleSubst_serialize (hb_serialize_context_t *c, + Iterator it); + +} +} +} + +#endif /* OT_LAYOUT_GSUB_COMMON_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/ContextSubst.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/ContextSubst.hh new file mode 100644 index 00000000000..9f8cb46b5e2 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/ContextSubst.hh @@ -0,0 +1,18 @@ +#ifndef OT_LAYOUT_GSUB_CONTEXTSUBST_HH +#define OT_LAYOUT_GSUB_CONTEXTSUBST_HH + +// TODO(garretrieger): move to new layout. +#include "../../../hb-ot-layout-gsubgpos.hh" +#include "Common.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +struct ContextSubst : Context {}; + +} +} +} + +#endif /* OT_LAYOUT_GSUB_CONTEXTSUBST_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/ExtensionSubst.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/ExtensionSubst.hh new file mode 100644 index 00000000000..831a7dfa2d1 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/ExtensionSubst.hh @@ -0,0 +1,22 @@ +#ifndef OT_LAYOUT_GSUB_EXTENSIONSUBST_HH +#define OT_LAYOUT_GSUB_EXTENSIONSUBST_HH + +// TODO(garretrieger): move to new layout. +#include "../../../hb-ot-layout-gsubgpos.hh" +#include "Common.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +struct ExtensionSubst : Extension +{ + typedef struct SubstLookupSubTable SubTable; + bool is_reverse () const; +}; + +} +} +} + +#endif /* OT_LAYOUT_GSUB_EXTENSIONSUBST_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/GSUB.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/GSUB.hh new file mode 100644 index 00000000000..900cf603e45 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/GSUB.hh @@ -0,0 +1,61 @@ +#ifndef OT_LAYOUT_GSUB_GSUB_HH +#define OT_LAYOUT_GSUB_GSUB_HH + +#include "../../../hb-ot-layout-gsubgpos.hh" +#include "Common.hh" +#include "SubstLookup.hh" + +namespace OT { + +using Layout::GSUB_impl::SubstLookup; + +namespace Layout { + +/* + * GSUB -- Glyph Substitution + * https://docs.microsoft.com/en-us/typography/opentype/spec/gsub + */ + +struct GSUB : GSUBGPOS +{ + using Lookup = SubstLookup; + + static constexpr hb_tag_t tableTag = HB_OT_TAG_GSUB; + + const SubstLookup& get_lookup (unsigned int i) const + { return static_cast (GSUBGPOS::get_lookup (i)); } + + bool subset (hb_subset_context_t *c) const + { + hb_subset_layout_context_t l (c, tableTag); + return GSUBGPOS::subset (&l); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (GSUBGPOS::sanitize (c)); + } + + HB_INTERNAL bool is_blocklisted (hb_blob_t *blob, + hb_face_t *face) const; + + void closure_lookups (hb_face_t *face, + const hb_set_t *glyphs, + hb_set_t *lookup_indexes /* IN/OUT */) const + { GSUBGPOS::closure_lookups (face, glyphs, lookup_indexes); } + + typedef GSUBGPOS::accelerator_t accelerator_t; +}; + + +} + +struct GSUB_accelerator_t : Layout::GSUB::accelerator_t { + GSUB_accelerator_t (hb_face_t *face) : Layout::GSUB::accelerator_t (face) {} +}; + + +} + +#endif /* OT_LAYOUT_GSUB_GSUB_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/Ligature.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/Ligature.hh new file mode 100644 index 00000000000..402ed12ae20 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/Ligature.hh @@ -0,0 +1,190 @@ +#ifndef OT_LAYOUT_GSUB_LIGATURE_HH +#define OT_LAYOUT_GSUB_LIGATURE_HH + +#include "Common.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +template +struct Ligature +{ + public: + typename Types::HBGlyphID + ligGlyph; /* GlyphID of ligature to substitute */ + HeadlessArray16Of + component; /* Array of component GlyphIDs--start + * with the second component--ordered + * in writing direction */ + public: + DEFINE_SIZE_ARRAY (Types::size + 2, component); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (ligGlyph.sanitize (c) && component.sanitize (c)); + } + + bool intersects (const hb_set_t *glyphs) const + { return hb_all (component, glyphs); } + + bool intersects_lig_glyph (const hb_set_t *glyphs) const + { return glyphs->has(ligGlyph); } + + void closure (hb_closure_context_t *c) const + { + if (!intersects (c->glyphs)) return; + c->output->add (ligGlyph); + } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + c->input->add_array (component.arrayZ, component.get_length ()); + c->output->add (ligGlyph); + } + + bool would_apply (hb_would_apply_context_t *c) const + { + if (c->len != component.lenP1) + return false; + + for (unsigned int i = 1; i < c->len; i++) + if (likely (c->glyphs[i] != component[i])) + return false; + + return true; + } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + unsigned int count = component.lenP1; + + if (unlikely (!count)) return_trace (false); + + /* Special-case to make it in-place and not consider this + * as a "ligated" substitution. */ + if (unlikely (count == 1)) + { + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->sync_so_far (); + c->buffer->message (c->font, + "replacing glyph at %u (ligature substitution)", + c->buffer->idx); + } + + c->replace_glyph (ligGlyph); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "replaced glyph at %u (ligature substitution)", + c->buffer->idx - 1u); + } + + return_trace (true); + } + + unsigned int total_component_count = 0; + + unsigned int match_end = 0; + unsigned int match_positions[HB_MAX_CONTEXT_LENGTH]; + + if (likely (!match_input (c, count, + &component[1], + match_glyph, + nullptr, + &match_end, + match_positions, + &total_component_count))) + { + c->buffer->unsafe_to_concat (c->buffer->idx, match_end); + return_trace (false); + } + + unsigned pos = 0; + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + unsigned delta = c->buffer->sync_so_far (); + + pos = c->buffer->idx; + + char buf[HB_MAX_CONTEXT_LENGTH * 16] = {0}; + char *p = buf; + + match_end += delta; + for (unsigned i = 0; i < count; i++) + { + match_positions[i] += delta; + if (i) + *p++ = ','; + snprintf (p, sizeof(buf) - (p - buf), "%u", match_positions[i]); + p += strlen(p); + } + + c->buffer->message (c->font, + "ligating glyphs at %s", + buf); + } + + ligate_input (c, + count, + match_positions, + match_end, + ligGlyph, + total_component_count); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->sync_so_far (); + c->buffer->message (c->font, + "ligated glyph at %u", + pos); + } + + return_trace (true); + } + + template + bool serialize (hb_serialize_context_t *c, + hb_codepoint_t ligature, + Iterator components /* Starting from second */) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (this))) return_trace (false); + ligGlyph = ligature; + if (unlikely (!component.serialize (c, components))) return_trace (false); + return_trace (true); + } + + bool subset (hb_subset_context_t *c, unsigned coverage_idx) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + if (!intersects (&glyphset) || !glyphset.has (ligGlyph)) return_trace (false); + // Ensure Coverage table is always packed after this. + c->serializer->add_virtual_link (coverage_idx); + + auto it = + + hb_iter (component) + | hb_map (glyph_map) + ; + + auto *out = c->serializer->start_embed (*this); + return_trace (out->serialize (c->serializer, + glyph_map[ligGlyph], + it)); } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GSUB_LIGATURE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/LigatureSet.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/LigatureSet.hh new file mode 100644 index 00000000000..08665438c4a --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/LigatureSet.hh @@ -0,0 +1,188 @@ +#ifndef OT_LAYOUT_GSUB_LIGATURESET_HH +#define OT_LAYOUT_GSUB_LIGATURESET_HH + +#include "Common.hh" +#include "Ligature.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +template +struct LigatureSet +{ + protected: + Array16OfOffset16To> + ligature; /* Array LigatureSet tables + * ordered by preference */ + public: + DEFINE_SIZE_ARRAY (2, ligature); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (ligature.sanitize (c, this)); + } + + bool intersects (const hb_set_t *glyphs) const + { + return + + hb_iter (ligature) + | hb_map (hb_add (this)) + | hb_map ([glyphs] (const Ligature &_) { return _.intersects (glyphs); }) + | hb_any + ; + } + + bool intersects_lig_glyph (const hb_set_t *glyphs) const + { + return + + hb_iter (ligature) + | hb_map (hb_add (this)) + | hb_map ([glyphs] (const Ligature &_) { + return _.intersects_lig_glyph (glyphs) && _.intersects (glyphs); + }) + | hb_any + ; + } + + void closure (hb_closure_context_t *c) const + { + + hb_iter (ligature) + | hb_map (hb_add (this)) + | hb_apply ([c] (const Ligature &_) { _.closure (c); }) + ; + } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + + hb_iter (ligature) + | hb_map (hb_add (this)) + | hb_apply ([c] (const Ligature &_) { _.collect_glyphs (c); }) + ; + } + + bool would_apply (hb_would_apply_context_t *c) const + { + return + + hb_iter (ligature) + | hb_map (hb_add (this)) + | hb_map ([c] (const Ligature &_) { return _.would_apply (c); }) + | hb_any + ; + } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + + unsigned int num_ligs = ligature.len; + +#ifndef HB_NO_OT_RULESETS_FAST_PATH + if (HB_OPTIMIZE_SIZE_VAL || num_ligs <= 4) +#endif + { + slow: + for (unsigned int i = 0; i < num_ligs; i++) + { + const auto &lig = this+ligature.arrayZ[i]; + if (lig.apply (c)) return_trace (true); + } + return_trace (false); + } + + /* This version is optimized for speed by matching the first component + * of the ligature here, instead of calling into the ligation code. + * + * This is replicated in ChainRuleSet and RuleSet. */ + + hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + skippy_iter.reset (c->buffer->idx); + skippy_iter.set_match_func (match_always, nullptr); + skippy_iter.set_glyph_data ((HBUINT16 *) nullptr); + unsigned unsafe_to; + hb_codepoint_t first = (unsigned) -1; + bool matched = skippy_iter.next (&unsafe_to); + if (likely (matched)) + { + first = c->buffer->info[skippy_iter.idx].codepoint; + unsafe_to = skippy_iter.idx + 1; + + if (skippy_iter.may_skip (c->buffer->info[skippy_iter.idx])) + { + /* Can't use the fast path if eg. the next char is a default-ignorable + * or other skippable. */ + goto slow; + } + } + else + goto slow; + + bool unsafe_to_concat = false; + + for (unsigned int i = 0; i < num_ligs; i++) + { + const auto &lig = this+ligature.arrayZ[i]; + if (unlikely (lig.component.lenP1 <= 1) || + lig.component.arrayZ[0] == first) + { + if (lig.apply (c)) + { + if (unsafe_to_concat) + c->buffer->unsafe_to_concat (c->buffer->idx, unsafe_to); + return_trace (true); + } + } + else if (likely (lig.component.lenP1 > 1)) + unsafe_to_concat = true; + } + if (likely (unsafe_to_concat)) + c->buffer->unsafe_to_concat (c->buffer->idx, unsafe_to); + + return_trace (false); + } + + bool serialize (hb_serialize_context_t *c, + hb_array_t ligatures, + hb_array_t component_count_list, + hb_array_t &component_list /* Starting from second for each ligature */) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (this))) return_trace (false); + if (unlikely (!ligature.serialize (c, ligatures.length))) return_trace (false); + for (unsigned int i = 0; i < ligatures.length; i++) + { + unsigned int component_count = (unsigned) hb_max ((int) component_count_list[i] - 1, 0); + if (unlikely (!ligature[i].serialize_serialize (c, + ligatures[i], + component_list.sub_array (0, component_count)))) + return_trace (false); + component_list += component_count; + } + return_trace (true); + } + + bool subset (hb_subset_context_t *c, unsigned coverage_idx) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + + hb_iter (ligature) + | hb_filter (subset_offset_array (c, out->ligature, this, coverage_idx)) + | hb_drain + ; + + if (bool (out->ligature)) + // Ensure Coverage table is always packed after this. + c->serializer->add_virtual_link (coverage_idx); + + return_trace (bool (out->ligature)); + } +}; + +} +} +} + +#endif /* OT_LAYOUT_GSUB_LIGATURESET_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/LigatureSubst.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/LigatureSubst.hh new file mode 100644 index 00000000000..18f6e355816 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/LigatureSubst.hh @@ -0,0 +1,71 @@ +#ifndef OT_LAYOUT_GSUB_LIGATURESUBST_HH +#define OT_LAYOUT_GSUB_LIGATURESUBST_HH + +#include "Common.hh" +#include "LigatureSubstFormat1.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +struct LigatureSubst +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + LigatureSubstFormat1_2 format1; +#ifndef HB_NO_BEYOND_64K + LigatureSubstFormat1_2 format2; +#endif + } u; + + public: + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); +#ifndef HB_NO_BEYOND_64K + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); +#endif + default:return_trace (c->default_return_value ()); + } + } + + /* TODO This function is only used by small GIDs, and not updated to 24bit GIDs. Should + * be done by using iterators. While at it perhaps using iterator of arrays of hb_codepoint_t + * instead. */ + bool serialize (hb_serialize_context_t *c, + hb_sorted_array_t first_glyphs, + hb_array_t ligature_per_first_glyph_count_list, + hb_array_t ligatures_list, + hb_array_t component_count_list, + hb_array_t component_list /* Starting from second for each ligature */) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (u.format))) return_trace (false); + unsigned int format = 1; + u.format = format; + switch (u.format) { + case 1: return_trace (u.format1.serialize (c, + first_glyphs, + ligature_per_first_glyph_count_list, + ligatures_list, + component_count_list, + component_list)); + default:return_trace (false); + } + } + + /* TODO subset() should choose format. */ + +}; + + +} +} +} + +#endif /* OT_LAYOUT_GSUB_LIGATURESUBST_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/LigatureSubstFormat1.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/LigatureSubstFormat1.hh new file mode 100644 index 00000000000..5c7df97d13a --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/LigatureSubstFormat1.hh @@ -0,0 +1,166 @@ +#ifndef OT_LAYOUT_GSUB_LIGATURESUBSTFORMAT1_HH +#define OT_LAYOUT_GSUB_LIGATURESUBSTFORMAT1_HH + +#include "Common.hh" +#include "LigatureSet.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +template +struct LigatureSubstFormat1_2 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + typename Types::template OffsetTo + coverage; /* Offset to Coverage table--from + * beginning of Substitution table */ + Array16Of>> + ligatureSet; /* Array LigatureSet tables + * ordered by Coverage Index */ + public: + DEFINE_SIZE_ARRAY (4 + Types::size, ligatureSet); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (coverage.sanitize (c, this) && ligatureSet.sanitize (c, this)); + } + + bool intersects (const hb_set_t *glyphs) const + { + return + + hb_zip (this+coverage, ligatureSet) + | hb_filter (*glyphs, hb_first) + | hb_map (hb_second) + | hb_map ([this, glyphs] (const typename Types::template OffsetTo> &_) + { return (this+_).intersects (glyphs); }) + | hb_any + ; + } + + bool may_have_non_1to1 () const + { return true; } + + void closure (hb_closure_context_t *c) const + { + + hb_zip (this+coverage, ligatureSet) + | hb_filter (c->parent_active_glyphs (), hb_first) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([c] (const LigatureSet &_) { _.closure (c); }) + ; + + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; + + + hb_zip (this+coverage, ligatureSet) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([c] (const LigatureSet &_) { _.collect_glyphs (c); }) + ; + } + + const Coverage &get_coverage () const { return this+coverage; } + + bool would_apply (hb_would_apply_context_t *c) const + { + unsigned int index = (this+coverage).get_coverage (c->glyphs[0]); + if (likely (index == NOT_COVERED)) return false; + + const auto &lig_set = this+ligatureSet[index]; + return lig_set.would_apply (c); + } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + + unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + const auto &lig_set = this+ligatureSet[index]; + return_trace (lig_set.apply (c)); + } + + bool serialize (hb_serialize_context_t *c, + hb_sorted_array_t first_glyphs, + hb_array_t ligature_per_first_glyph_count_list, + hb_array_t ligatures_list, + hb_array_t component_count_list, + hb_array_t component_list /* Starting from second for each ligature */) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (this))) return_trace (false); + if (unlikely (!ligatureSet.serialize (c, first_glyphs.length))) return_trace (false); + for (unsigned int i = 0; i < first_glyphs.length; i++) + { + unsigned int ligature_count = ligature_per_first_glyph_count_list[i]; + if (unlikely (!ligatureSet[i] + .serialize_serialize (c, + ligatures_list.sub_array (0, ligature_count), + component_count_list.sub_array (0, ligature_count), + component_list))) return_trace (false); + ligatures_list += ligature_count; + component_count_list += ligature_count; + } + return_trace (coverage.serialize_serialize (c, first_glyphs)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + // Due to a bug in some older versions of windows 7 the Coverage table must be + // packed after the LigatureSet and Ligature tables, so serialize Coverage first + // which places it last in the packed order. + hb_set_t new_coverage; + + hb_zip (this+coverage, hb_iter (ligatureSet) | hb_map (hb_add (this))) + | hb_filter (glyphset, hb_first) + | hb_filter ([&] (const LigatureSet& _) { + return _.intersects_lig_glyph (&glyphset); + }, hb_second) + | hb_map (hb_first) + | hb_sink (new_coverage); + + if (!c->serializer->push () + ->serialize (c->serializer, + + new_coverage.iter () | hb_map_retains_sorting (glyph_map))) + { + c->serializer->pop_discard (); + return_trace (false); + } + + unsigned coverage_idx = c->serializer->pop_pack (); + c->serializer->add_link (out->coverage, coverage_idx); + + + hb_zip (this+coverage, ligatureSet) + | hb_filter (new_coverage, hb_first) + | hb_map (hb_second) + // to ensure that the repacker always orders the coverage table after the LigatureSet + // and LigatureSubtable's they will be linked to the Coverage table via a virtual link + // the coverage table object idx is passed down to facilitate this. + | hb_apply (subset_offset_array (c, out->ligatureSet, this, coverage_idx)) + ; + + return_trace (bool (new_coverage)); + } +}; + +} +} +} + +#endif /* OT_LAYOUT_GSUB_LIGATURESUBSTFORMAT1_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/MultipleSubst.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/MultipleSubst.hh new file mode 100644 index 00000000000..742c8587ee3 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/MultipleSubst.hh @@ -0,0 +1,62 @@ +#ifndef OT_LAYOUT_GSUB_MULTIPLESUBST_HH +#define OT_LAYOUT_GSUB_MULTIPLESUBST_HH + +#include "Common.hh" +#include "MultipleSubstFormat1.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +struct MultipleSubst +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + MultipleSubstFormat1_2 format1; +#ifndef HB_NO_BEYOND_64K + MultipleSubstFormat1_2 format2; +#endif + } u; + + public: + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); +#ifndef HB_NO_BEYOND_64K + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); +#endif + default:return_trace (c->default_return_value ()); + } + } + + template + bool serialize (hb_serialize_context_t *c, + Iterator it) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (u.format))) return_trace (false); + unsigned int format = 1; + u.format = format; + switch (u.format) { + case 1: return_trace (u.format1.serialize (c, it)); + default:return_trace (false); + } + } + + /* TODO subset() should choose format. */ + +}; + + +} +} +} + +#endif /* OT_LAYOUT_GSUB_MULTIPLESUBST_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/MultipleSubstFormat1.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/MultipleSubstFormat1.hh new file mode 100644 index 00000000000..3b4bd116949 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/MultipleSubstFormat1.hh @@ -0,0 +1,130 @@ +#ifndef OT_LAYOUT_GSUB_MULTIPLESUBSTFORMAT1_HH +#define OT_LAYOUT_GSUB_MULTIPLESUBSTFORMAT1_HH + +#include "Common.hh" +#include "Sequence.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +template +struct MultipleSubstFormat1_2 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + typename Types::template OffsetTo + coverage; /* Offset to Coverage table--from + * beginning of Substitution table */ + Array16Of>> + sequence; /* Array of Sequence tables + * ordered by Coverage Index */ + public: + DEFINE_SIZE_ARRAY (4 + Types::size, sequence); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (coverage.sanitize (c, this) && sequence.sanitize (c, this)); + } + + bool intersects (const hb_set_t *glyphs) const + { return (this+coverage).intersects (glyphs); } + + bool may_have_non_1to1 () const + { return true; } + + void closure (hb_closure_context_t *c) const + { + + hb_zip (this+coverage, sequence) + | hb_filter (c->parent_active_glyphs (), hb_first) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([c] (const Sequence &_) { _.closure (c); }) + ; + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; + + hb_zip (this+coverage, sequence) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([c] (const Sequence &_) { _.collect_glyphs (c); }) + ; + } + + const Coverage &get_coverage () const { return this+coverage; } + + bool would_apply (hb_would_apply_context_t *c) const + { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + + unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + return_trace ((this+sequence[index]).apply (c)); + } + + template + bool serialize (hb_serialize_context_t *c, + Iterator it) + { + TRACE_SERIALIZE (this); + auto sequences = + + it + | hb_map (hb_second) + ; + auto glyphs = + + it + | hb_map_retains_sorting (hb_first) + ; + if (unlikely (!c->extend_min (this))) return_trace (false); + + if (unlikely (!sequence.serialize (c, sequences.length))) return_trace (false); + + for (auto& pair : hb_zip (sequences, sequence)) + { + if (unlikely (!pair.second + .serialize_serialize (c, pair.first))) + return_trace (false); + } + + return_trace (coverage.serialize_serialize (c, glyphs)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + hb_sorted_vector_t new_coverage; + + hb_zip (this+coverage, sequence) + | hb_filter (glyphset, hb_first) + | hb_filter (subset_offset_array (c, out->sequence, this), hb_second) + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + out->coverage.serialize_serialize (c->serializer, new_coverage.iter ()); + return_trace (bool (new_coverage)); + } +}; + +} +} +} + + +#endif /* OT_LAYOUT_GSUB_MULTIPLESUBSTFORMAT1_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/ReverseChainSingleSubst.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/ReverseChainSingleSubst.hh new file mode 100644 index 00000000000..5ad463fea79 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/ReverseChainSingleSubst.hh @@ -0,0 +1,36 @@ +#ifndef OT_LAYOUT_GSUB_REVERSECHAINSINGLESUBST_HH +#define OT_LAYOUT_GSUB_REVERSECHAINSINGLESUBST_HH + +#include "Common.hh" +#include "ReverseChainSingleSubstFormat1.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +struct ReverseChainSingleSubst +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + ReverseChainSingleSubstFormat1 format1; + } u; + + public: + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); + default:return_trace (c->default_return_value ()); + } + } +}; + +} +} +} + +#endif /* HB_OT_LAYOUT_GSUB_REVERSECHAINSINGLESUBST_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh new file mode 100644 index 00000000000..916fa281b32 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh @@ -0,0 +1,243 @@ +#ifndef OT_LAYOUT_GSUB_REVERSECHAINSINGLESUBSTFORMAT1_HH +#define OT_LAYOUT_GSUB_REVERSECHAINSINGLESUBSTFORMAT1_HH + +#include "Common.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +struct ReverseChainSingleSubstFormat1 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + Offset16To + coverage; /* Offset to Coverage table--from + * beginning of table */ + Array16OfOffset16To + backtrack; /* Array of coverage tables + * in backtracking sequence, in glyph + * sequence order */ + Array16OfOffset16To + lookaheadX; /* Array of coverage tables + * in lookahead sequence, in glyph + * sequence order */ + Array16Of + substituteX; /* Array of substitute + * GlyphIDs--ordered by Coverage Index */ + public: + DEFINE_SIZE_MIN (10); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!(coverage.sanitize (c, this) && backtrack.sanitize (c, this))) + return_trace (false); + const auto &lookahead = StructAfter (backtrack); + if (!lookahead.sanitize (c, this)) + return_trace (false); + const auto &substitute = StructAfter (lookahead); + return_trace (substitute.sanitize (c)); + } + + bool intersects (const hb_set_t *glyphs) const + { + if (!(this+coverage).intersects (glyphs)) + return false; + + const auto &lookahead = StructAfter (backtrack); + + unsigned int count; + + count = backtrack.len; + for (unsigned int i = 0; i < count; i++) + if (!(this+backtrack[i]).intersects (glyphs)) + return false; + + count = lookahead.len; + for (unsigned int i = 0; i < count; i++) + if (!(this+lookahead[i]).intersects (glyphs)) + return false; + + return true; + } + + bool may_have_non_1to1 () const + { return false; } + + void closure (hb_closure_context_t *c) const + { + if (!intersects (c->glyphs)) return; + + const auto &lookahead = StructAfter (backtrack); + const auto &substitute = StructAfter (lookahead); + + + hb_zip (this+coverage, substitute) + | hb_filter (c->parent_active_glyphs (), hb_first) + | hb_map (hb_second) + | hb_sink (c->output) + ; + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; + + unsigned int count; + + count = backtrack.len; + for (unsigned int i = 0; i < count; i++) + if (unlikely (!(this+backtrack[i]).collect_coverage (c->before))) return; + + const auto &lookahead = StructAfter (backtrack); + count = lookahead.len; + for (unsigned int i = 0; i < count; i++) + if (unlikely (!(this+lookahead[i]).collect_coverage (c->after))) return; + + const auto &substitute = StructAfter (lookahead); + count = substitute.len; + c->output->add_array (substitute.arrayZ, substitute.len); + } + + const Coverage &get_coverage () const { return this+coverage; } + + bool would_apply (hb_would_apply_context_t *c) const + { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + if (unlikely (c->nesting_level_left != HB_MAX_NESTING_LEVEL)) + return_trace (false); /* No chaining to this type */ + + unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + const auto &lookahead = StructAfter (backtrack); + const auto &substitute = StructAfter (lookahead); + + if (unlikely (index >= substitute.len)) return_trace (false); + + unsigned int start_index = 0, end_index = 0; + if (match_backtrack (c, + backtrack.len, (HBUINT16 *) backtrack.arrayZ, + match_coverage, this, + &start_index) && + match_lookahead (c, + lookahead.len, (HBUINT16 *) lookahead.arrayZ, + match_coverage, this, + c->buffer->idx + 1, &end_index)) + { + c->buffer->unsafe_to_break_from_outbuffer (start_index, end_index); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "replacing glyph at %u (reverse chaining substitution)", + c->buffer->idx); + } + + c->replace_glyph_inplace (substitute[index]); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "replaced glyph at %u (reverse chaining substitution)", + c->buffer->idx); + } + + /* Note: We DON'T decrease buffer->idx. The main loop does it + * for us. This is useful for preventing surprises if someone + * calls us through a Context lookup. */ + return_trace (true); + } + else + { + c->buffer->unsafe_to_concat_from_outbuffer (start_index, end_index); + return_trace (false); + } + } + + template + bool serialize_coverage_offset_array (hb_subset_context_t *c, Iterator it) const + { + TRACE_SERIALIZE (this); + auto *out = c->serializer->start_embed> (); + + if (unlikely (!c->serializer->allocate_size (HBUINT16::static_size))) + return_trace (false); + + for (auto& offset : it) { + auto *o = out->serialize_append (c->serializer); + if (unlikely (!o) || !o->serialize_subset (c, offset, this)) + return_trace (false); + } + + return_trace (true); + } + + template + bool serialize (hb_subset_context_t *c, + Iterator coverage_subst_iter, + BacktrackIterator backtrack_iter, + LookaheadIterator lookahead_iter) const + { + TRACE_SERIALIZE (this); + + auto *out = c->serializer->start_embed (this); + if (unlikely (!c->serializer->embed (this->format))) return_trace (false); + if (unlikely (!c->serializer->embed (this->coverage))) return_trace (false); + + if (!serialize_coverage_offset_array (c, backtrack_iter)) return_trace (false); + if (!serialize_coverage_offset_array (c, lookahead_iter)) return_trace (false); + + auto *substitute_out = c->serializer->start_embed> (); + auto substitutes = + + coverage_subst_iter + | hb_map (hb_second) + ; + + auto glyphs = + + coverage_subst_iter + | hb_map_retains_sorting (hb_first) + ; + if (unlikely (! c->serializer->check_success (substitute_out->serialize (c->serializer, substitutes)))) + return_trace (false); + + if (unlikely (!out->coverage.serialize_serialize (c->serializer, glyphs))) + return_trace (false); + return_trace (true); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + const auto &lookahead = StructAfter (backtrack); + const auto &substitute = StructAfter (lookahead); + + auto it = + + hb_zip (this+coverage, substitute) + | hb_filter (glyphset, hb_first) + | hb_filter (glyphset, hb_second) + | hb_map_retains_sorting ([&] (hb_pair_t p) -> hb_codepoint_pair_t + { return hb_pair (glyph_map[p.first], glyph_map[p.second]); }) + ; + + return_trace (bool (it) && serialize (c, it, backtrack.iter (), lookahead.iter ())); + } +}; + +} +} +} + +#endif /* HB_OT_LAYOUT_GSUB_REVERSECHAINSINGLESUBSTFORMAT1_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/Sequence.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/Sequence.hh new file mode 100644 index 00000000000..a26cf8c6a65 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/Sequence.hh @@ -0,0 +1,165 @@ +#ifndef OT_LAYOUT_GSUB_SEQUENCE_HH +#define OT_LAYOUT_GSUB_SEQUENCE_HH + +#include "Common.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +template +struct Sequence +{ + protected: + Array16Of + substitute; /* String of GlyphIDs to substitute */ + public: + DEFINE_SIZE_ARRAY (2, substitute); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (substitute.sanitize (c)); + } + + bool intersects (const hb_set_t *glyphs) const + { return hb_all (substitute, glyphs); } + + void closure (hb_closure_context_t *c) const + { c->output->add_array (substitute.arrayZ, substitute.len); } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { c->output->add_array (substitute.arrayZ, substitute.len); } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + unsigned int count = substitute.len; + + /* Special-case to make it in-place and not consider this + * as a "multiplied" substitution. */ + if (unlikely (count == 1)) + { + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->sync_so_far (); + c->buffer->message (c->font, + "replacing glyph at %u (multiple substitution)", + c->buffer->idx); + } + + c->replace_glyph (substitute.arrayZ[0]); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "replaced glyph at %u (multiple substitution)", + c->buffer->idx - 1u); + } + + return_trace (true); + } + /* Spec disallows this, but Uniscribe allows it. + * https://github.com/harfbuzz/harfbuzz/issues/253 */ + else if (unlikely (count == 0)) + { + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->sync_so_far (); + c->buffer->message (c->font, + "deleting glyph at %u (multiple substitution)", + c->buffer->idx); + } + + c->buffer->delete_glyph (); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->sync_so_far (); + c->buffer->message (c->font, + "deleted glyph at %u (multiple substitution)", + c->buffer->idx); + } + + return_trace (true); + } + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->sync_so_far (); + c->buffer->message (c->font, + "multiplying glyph at %u", + c->buffer->idx); + } + + unsigned int klass = _hb_glyph_info_is_ligature (&c->buffer->cur()) ? + HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH : 0; + unsigned lig_id = _hb_glyph_info_get_lig_id (&c->buffer->cur()); + + for (unsigned int i = 0; i < count; i++) + { + /* If is attached to a ligature, don't disturb that. + * https://github.com/harfbuzz/harfbuzz/issues/3069 */ + if (!lig_id) + _hb_glyph_info_set_lig_props_for_component (&c->buffer->cur(), i); + c->output_glyph_for_component (substitute.arrayZ[i], klass); + } + c->buffer->skip_glyph (); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->sync_so_far (); + + char buf[HB_MAX_CONTEXT_LENGTH * 16] = {0}; + char *p = buf; + + for (unsigned i = c->buffer->idx - count; i < c->buffer->idx; i++) + { + if (buf < p) + *p++ = ','; + snprintf (p, sizeof(buf) - (p - buf), "%u", i); + p += strlen(p); + } + + c->buffer->message (c->font, + "multiplied glyphs at %s", + buf); + } + + return_trace (true); + } + + template + bool serialize (hb_serialize_context_t *c, + Iterator subst) + { + TRACE_SERIALIZE (this); + return_trace (substitute.serialize (c, subst)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + if (!intersects (&glyphset)) return_trace (false); + + auto it = + + hb_iter (substitute) + | hb_map (glyph_map) + ; + + auto *out = c->serializer->start_embed (*this); + return_trace (out->serialize (c->serializer, it)); + } +}; + + +} +} +} + + +#endif /* OT_LAYOUT_GSUB_SEQUENCE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/SingleSubst.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/SingleSubst.hh new file mode 100644 index 00000000000..181c9e52e5c --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/SingleSubst.hh @@ -0,0 +1,103 @@ +#ifndef OT_LAYOUT_GSUB_SINGLESUBST_HH +#define OT_LAYOUT_GSUB_SINGLESUBST_HH + +#include "Common.hh" +#include "SingleSubstFormat1.hh" +#include "SingleSubstFormat2.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +struct SingleSubst +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + SingleSubstFormat1_3 format1; + SingleSubstFormat2_4 format2; +#ifndef HB_NO_BEYOND_64K + SingleSubstFormat1_3 format3; + SingleSubstFormat2_4 format4; +#endif + } u; + + public: + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); +#ifndef HB_NO_BEYOND_64K + case 3: return_trace (c->dispatch (u.format3, std::forward (ds)...)); + case 4: return_trace (c->dispatch (u.format4, std::forward (ds)...)); +#endif + default:return_trace (c->default_return_value ()); + } + } + + template + bool serialize (hb_serialize_context_t *c, + Iterator glyphs) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (u.format))) return_trace (false); + unsigned format = 2; + unsigned delta = 0; + if (glyphs) + { + format = 1; + hb_codepoint_t mask = 0xFFFFu; + +#ifndef HB_NO_BEYOND_64K + if (+ glyphs + | hb_map_retains_sorting (hb_second) + | hb_filter ([] (hb_codepoint_t gid) { return gid > 0xFFFFu; })) + { + format += 2; + mask = 0xFFFFFFu; + } +#endif + + auto get_delta = [=] (hb_codepoint_pair_t _) + { return (unsigned) (_.second - _.first) & mask; }; + delta = get_delta (*glyphs); + if (!hb_all (++(+glyphs), delta, get_delta)) format += 1; + } + + u.format = format; + switch (u.format) { + case 1: return_trace (u.format1.serialize (c, + + glyphs + | hb_map_retains_sorting (hb_first), + delta)); + case 2: return_trace (u.format2.serialize (c, glyphs)); +#ifndef HB_NO_BEYOND_64K + case 3: return_trace (u.format3.serialize (c, + + glyphs + | hb_map_retains_sorting (hb_first), + delta)); + case 4: return_trace (u.format4.serialize (c, glyphs)); +#endif + default:return_trace (false); + } + } +}; + +template +static void +SingleSubst_serialize (hb_serialize_context_t *c, + Iterator it) +{ c->start_embed ()->serialize (c, it); } + +} +} +} + +#endif /* OT_LAYOUT_GSUB_SINGLESUBST_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/SingleSubstFormat1.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/SingleSubstFormat1.hh new file mode 100644 index 00000000000..850be86c043 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/SingleSubstFormat1.hh @@ -0,0 +1,204 @@ +#ifndef OT_LAYOUT_GSUB_SINGLESUBSTFORMAT1_HH +#define OT_LAYOUT_GSUB_SINGLESUBSTFORMAT1_HH + +#include "Common.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +template +struct SingleSubstFormat1_3 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + typename Types::template OffsetTo + coverage; /* Offset to Coverage table--from + * beginning of Substitution table */ + typename Types::HBUINT + deltaGlyphID; /* Add to original GlyphID to get + * substitute GlyphID, modulo 0x10000 */ + + public: + DEFINE_SIZE_STATIC (2 + 2 * Types::size); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + coverage.sanitize (c, this) && + /* The coverage table may use a range to represent a set + * of glyphs, which means a small number of bytes can + * generate a large glyph set. Manually modify the + * sanitizer max ops to take this into account. + * + * Note: This check *must* be right after coverage sanitize. */ + c->check_ops ((this + coverage).get_population () >> 1)); + } + + hb_codepoint_t get_mask () const + { return (1 << (8 * Types::size)) - 1; } + + bool intersects (const hb_set_t *glyphs) const + { return (this+coverage).intersects (glyphs); } + + bool may_have_non_1to1 () const + { return false; } + + void closure (hb_closure_context_t *c) const + { + hb_codepoint_t d = deltaGlyphID; + hb_codepoint_t mask = get_mask (); + + /* Help fuzzer avoid this function as much. */ + unsigned pop = (this+coverage).get_population (); + if (pop >= mask) + return; + + hb_set_t intersection; + (this+coverage).intersect_set (c->parent_active_glyphs (), intersection); + + /* In degenerate fuzzer-found fonts, but not real fonts, + * this table can keep adding new glyphs in each round of closure. + * Refuse to close-over, if it maps glyph range to overlapping range. */ + hb_codepoint_t min_before = intersection.get_min (); + hb_codepoint_t max_before = intersection.get_max (); + hb_codepoint_t min_after = (min_before + d) & mask; + hb_codepoint_t max_after = (max_before + d) & mask; + if (intersection.get_population () == max_before - min_before + 1 && + ((min_before <= min_after && min_after <= max_before) || + (min_before <= max_after && max_after <= max_before))) + return; + + + hb_iter (intersection) + | hb_map ([d, mask] (hb_codepoint_t g) { return (g + d) & mask; }) + | hb_sink (c->output) + ; + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; + hb_codepoint_t d = deltaGlyphID; + hb_codepoint_t mask = get_mask (); + + + hb_iter (this+coverage) + | hb_map ([d, mask] (hb_codepoint_t g) { return (g + d) & mask; }) + | hb_sink (c->output) + ; + } + + const Coverage &get_coverage () const { return this+coverage; } + + bool would_apply (hb_would_apply_context_t *c) const + { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; } + + unsigned + get_glyph_alternates (hb_codepoint_t glyph_id, + unsigned start_offset, + unsigned *alternate_count /* IN/OUT. May be NULL. */, + hb_codepoint_t *alternate_glyphs /* OUT. May be NULL. */) const + { + unsigned int index = (this+coverage).get_coverage (glyph_id); + if (likely (index == NOT_COVERED)) + { + if (alternate_count) + *alternate_count = 0; + return 0; + } + + if (alternate_count && *alternate_count) + { + hb_codepoint_t d = deltaGlyphID; + hb_codepoint_t mask = get_mask (); + + glyph_id = (glyph_id + d) & mask; + + *alternate_glyphs = glyph_id; + *alternate_count = 1; + } + + return 1; + } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_codepoint_t glyph_id = c->buffer->cur().codepoint; + unsigned int index = (this+coverage).get_coverage (glyph_id); + if (likely (index == NOT_COVERED)) return_trace (false); + + hb_codepoint_t d = deltaGlyphID; + hb_codepoint_t mask = get_mask (); + + glyph_id = (glyph_id + d) & mask; + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->sync_so_far (); + c->buffer->message (c->font, + "replacing glyph at %u (single substitution)", + c->buffer->idx); + } + + c->replace_glyph (glyph_id); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "replaced glyph at %u (single substitution)", + c->buffer->idx - 1u); + } + + return_trace (true); + } + + template + bool serialize (hb_serialize_context_t *c, + Iterator glyphs, + unsigned delta) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (this))) return_trace (false); + if (unlikely (!coverage.serialize_serialize (c, glyphs))) return_trace (false); + c->check_assign (deltaGlyphID, delta, HB_SERIALIZE_ERROR_INT_OVERFLOW); + return_trace (true); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + hb_codepoint_t d = deltaGlyphID; + hb_codepoint_t mask = get_mask (); + + hb_set_t intersection; + (this+coverage).intersect_set (glyphset, intersection); + + auto it = + + hb_iter (intersection) + | hb_map_retains_sorting ([d, mask] (hb_codepoint_t g) { + return hb_codepoint_pair_t (g, + (g + d) & mask); }) + | hb_filter (glyphset, hb_second) + | hb_map_retains_sorting ([&] (hb_codepoint_pair_t p) -> hb_codepoint_pair_t + { return hb_pair (glyph_map[p.first], glyph_map[p.second]); }) + ; + + bool ret = bool (it); + SingleSubst_serialize (c->serializer, it); + return_trace (ret); + } +}; + +} +} +} + + +#endif /* OT_LAYOUT_GSUB_SINGLESUBSTFORMAT1_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/SingleSubstFormat2.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/SingleSubstFormat2.hh new file mode 100644 index 00000000000..9c651abe71d --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/SingleSubstFormat2.hh @@ -0,0 +1,176 @@ +#ifndef OT_LAYOUT_GSUB_SINGLESUBSTFORMAT2_HH +#define OT_LAYOUT_GSUB_SINGLESUBSTFORMAT2_HH + +#include "Common.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +template +struct SingleSubstFormat2_4 +{ + protected: + HBUINT16 format; /* Format identifier--format = 2 */ + typename Types::template OffsetTo + coverage; /* Offset to Coverage table--from + * beginning of Substitution table */ + Array16Of + substitute; /* Array of substitute + * GlyphIDs--ordered by Coverage Index */ + + public: + DEFINE_SIZE_ARRAY (4 + Types::size, substitute); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (coverage.sanitize (c, this) && substitute.sanitize (c)); + } + + bool intersects (const hb_set_t *glyphs) const + { return (this+coverage).intersects (glyphs); } + + bool may_have_non_1to1 () const + { return false; } + + void closure (hb_closure_context_t *c) const + { + auto &cov = this+coverage; + auto &glyph_set = c->parent_active_glyphs (); + + if (substitute.len > glyph_set.get_population () * 4) + { + for (auto g : glyph_set) + { + unsigned i = cov.get_coverage (g); + if (i == NOT_COVERED || i >= substitute.len) + continue; + c->output->add (substitute.arrayZ[i]); + } + + return; + } + + + hb_zip (cov, substitute) + | hb_filter (glyph_set, hb_first) + | hb_map (hb_second) + | hb_sink (c->output) + ; + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; + + hb_zip (this+coverage, substitute) + | hb_map (hb_second) + | hb_sink (c->output) + ; + } + + const Coverage &get_coverage () const { return this+coverage; } + + bool would_apply (hb_would_apply_context_t *c) const + { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; } + + unsigned + get_glyph_alternates (hb_codepoint_t glyph_id, + unsigned start_offset, + unsigned *alternate_count /* IN/OUT. May be NULL. */, + hb_codepoint_t *alternate_glyphs /* OUT. May be NULL. */) const + { + unsigned int index = (this+coverage).get_coverage (glyph_id); + if (likely (index == NOT_COVERED)) + { + if (alternate_count) + *alternate_count = 0; + return 0; + } + + if (alternate_count && *alternate_count) + { + glyph_id = substitute[index]; + + *alternate_glyphs = glyph_id; + *alternate_count = 1; + } + + return 1; + } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + if (unlikely (index >= substitute.len)) return_trace (false); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->sync_so_far (); + c->buffer->message (c->font, + "replacing glyph at %u (single substitution)", + c->buffer->idx); + } + + c->replace_glyph (substitute[index]); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "replaced glyph at %u (single substitution)", + c->buffer->idx - 1u); + } + + return_trace (true); + } + + template + bool serialize (hb_serialize_context_t *c, + Iterator it) + { + TRACE_SERIALIZE (this); + auto substitutes = + + it + | hb_map (hb_second) + ; + auto glyphs = + + it + | hb_map_retains_sorting (hb_first) + ; + if (unlikely (!c->extend_min (this))) return_trace (false); + if (unlikely (!substitute.serialize (c, substitutes))) return_trace (false); + if (unlikely (!coverage.serialize_serialize (c, glyphs))) return_trace (false); + return_trace (true); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto it = + + hb_zip (this+coverage, substitute) + | hb_filter (glyphset, hb_first) + | hb_filter (glyphset, hb_second) + | hb_map_retains_sorting ([&] (hb_pair_t p) -> hb_codepoint_pair_t + { return hb_pair (glyph_map[p.first], glyph_map[p.second]); }) + ; + + bool ret = bool (it); + SingleSubst_serialize (c->serializer, it); + return_trace (ret); + } +}; + +} +} +} + +#endif /* OT_LAYOUT_GSUB_SINGLESUBSTFORMAT2_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/SubstLookup.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/SubstLookup.hh new file mode 100644 index 00000000000..d49dcc0e0fd --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/SubstLookup.hh @@ -0,0 +1,220 @@ +#ifndef OT_LAYOUT_GSUB_SUBSTLOOKUP_HH +#define OT_LAYOUT_GSUB_SUBSTLOOKUP_HH + +#include "Common.hh" +#include "SubstLookupSubTable.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +struct SubstLookup : Lookup +{ + using SubTable = SubstLookupSubTable; + + bool sanitize (hb_sanitize_context_t *c) const + { return Lookup::sanitize (c); } + + const SubTable& get_subtable (unsigned int i) const + { return Lookup::get_subtable (i); } + + static inline bool lookup_type_is_reverse (unsigned int lookup_type) + { return lookup_type == SubTable::ReverseChainSingle; } + + bool is_reverse () const + { + unsigned int type = get_type (); + if (unlikely (type == SubTable::Extension)) + return get_subtable (0).u.extension.is_reverse (); + return lookup_type_is_reverse (type); + } + + bool may_have_non_1to1 () const + { + hb_have_non_1to1_context_t c; + return dispatch (&c); + } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + return_trace (dispatch (c)); + } + + bool intersects (const hb_set_t *glyphs) const + { + hb_intersects_context_t c (glyphs); + return dispatch (&c); + } + + hb_closure_context_t::return_t closure (hb_closure_context_t *c, unsigned int this_index) const + { + if (!c->should_visit_lookup (this_index)) + return hb_closure_context_t::default_return_value (); + + c->set_recurse_func (dispatch_closure_recurse_func); + + hb_closure_context_t::return_t ret = dispatch (c); + + c->flush (); + + return ret; + } + + hb_closure_lookups_context_t::return_t closure_lookups (hb_closure_lookups_context_t *c, unsigned this_index) const + { + if (c->is_lookup_visited (this_index)) + return hb_closure_lookups_context_t::default_return_value (); + + c->set_lookup_visited (this_index); + if (!intersects (c->glyphs)) + { + c->set_lookup_inactive (this_index); + return hb_closure_lookups_context_t::default_return_value (); + } + + hb_closure_lookups_context_t::return_t ret = dispatch (c); + return ret; + } + + hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const + { + c->set_recurse_func (dispatch_recurse_func); + return dispatch (c); + } + + template + void collect_coverage (set_t *glyphs) const + { + hb_collect_coverage_context_t c (glyphs); + dispatch (&c); + } + + bool would_apply (hb_would_apply_context_t *c, + const hb_ot_layout_lookup_accelerator_t *accel) const + { + if (unlikely (!c->len)) return false; + if (!accel->may_have (c->glyphs[0])) return false; + return dispatch (c); + } + + template + bool serialize_single (hb_serialize_context_t *c, + uint32_t lookup_props, + Glyphs glyphs, + Substitutes substitutes) + { + TRACE_SERIALIZE (this); + if (unlikely (!Lookup::serialize (c, SubTable::Single, lookup_props, 1))) return_trace (false); + if (c->push ()->u.single.serialize (c, hb_zip (glyphs, substitutes))) + { + c->add_link (get_subtables ()[0], c->pop_pack ()); + return_trace (true); + } + c->pop_discard (); + return_trace (false); + } + + template + bool serialize (hb_serialize_context_t *c, + uint32_t lookup_props, + Iterator it) + { + TRACE_SERIALIZE (this); + if (unlikely (!Lookup::serialize (c, SubTable::Multiple, lookup_props, 1))) return_trace (false); + if (c->push ()->u.multiple. + serialize (c, it)) + { + c->add_link (get_subtables ()[0], c->pop_pack ()); + return_trace (true); + } + c->pop_discard (); + return_trace (false); + } + + bool serialize_alternate (hb_serialize_context_t *c, + uint32_t lookup_props, + hb_sorted_array_t glyphs, + hb_array_t alternate_len_list, + hb_array_t alternate_glyphs_list) + { + TRACE_SERIALIZE (this); + if (unlikely (!Lookup::serialize (c, SubTable::Alternate, lookup_props, 1))) return_trace (false); + + if (c->push ()->u.alternate. + serialize (c, + glyphs, + alternate_len_list, + alternate_glyphs_list)) + { + c->add_link (get_subtables ()[0], c->pop_pack ()); + return_trace (true); + } + c->pop_discard (); + return_trace (false); + } + + bool serialize_ligature (hb_serialize_context_t *c, + uint32_t lookup_props, + hb_sorted_array_t first_glyphs, + hb_array_t ligature_per_first_glyph_count_list, + hb_array_t ligatures_list, + hb_array_t component_count_list, + hb_array_t component_list /* Starting from second for each ligature */) + { + TRACE_SERIALIZE (this); + if (unlikely (!Lookup::serialize (c, SubTable::Ligature, lookup_props, 1))) return_trace (false); + if (c->push ()->u.ligature. + serialize (c, + first_glyphs, + ligature_per_first_glyph_count_list, + ligatures_list, + component_count_list, + component_list)) + { + c->add_link (get_subtables ()[0], c->pop_pack ()); + return_trace (true); + } + c->pop_discard (); + return_trace (false); + } + + template + static inline typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index); + + static inline typename hb_closure_context_t::return_t closure_glyphs_recurse_func (hb_closure_context_t *c, unsigned lookup_index, hb_set_t *covered_seq_indices, unsigned seq_index, unsigned end_index); + + static inline hb_closure_context_t::return_t dispatch_closure_recurse_func (hb_closure_context_t *c, unsigned lookup_index, hb_set_t *covered_seq_indices, unsigned seq_index, unsigned end_index) + { + if (!c->should_visit_lookup (lookup_index)) + return hb_empty_t (); + + hb_closure_context_t::return_t ret = closure_glyphs_recurse_func (c, lookup_index, covered_seq_indices, seq_index, end_index); + + /* While in theory we should flush here, it will cause timeouts because a recursive + * lookup can keep growing the glyph set. Skip, and outer loop will retry up to + * HB_CLOSURE_MAX_STAGES time, which should be enough for every realistic font. */ + //c->flush (); + + return ret; + } + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { return Lookup::dispatch (c, std::forward (ds)...); } + + bool subset (hb_subset_context_t *c) const + { return Lookup::subset (c); } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GSUB_SUBSTLOOKUP_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/SubstLookupSubTable.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/SubstLookupSubTable.hh new file mode 100644 index 00000000000..a525fba0399 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/GSUB/SubstLookupSubTable.hh @@ -0,0 +1,77 @@ +#ifndef OT_LAYOUT_GSUB_SUBSTLOOKUPSUBTABLE_HH +#define OT_LAYOUT_GSUB_SUBSTLOOKUPSUBTABLE_HH + +#include "Common.hh" +#include "SingleSubst.hh" +#include "MultipleSubst.hh" +#include "AlternateSubst.hh" +#include "LigatureSubst.hh" +#include "ContextSubst.hh" +#include "ChainContextSubst.hh" +#include "ExtensionSubst.hh" +#include "ReverseChainSingleSubst.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +struct SubstLookupSubTable +{ + friend struct ::OT::Lookup; + friend struct SubstLookup; + + protected: + union { + SingleSubst single; + MultipleSubst multiple; + AlternateSubst alternate; + LigatureSubst ligature; + ContextSubst context; + ChainContextSubst chainContext; + ExtensionSubst extension; + ReverseChainSingleSubst reverseChainContextSingle; + } u; + public: + DEFINE_SIZE_MIN (0); + + enum Type { + Single = 1, + Multiple = 2, + Alternate = 3, + Ligature = 4, + Context = 5, + ChainContext = 6, + Extension = 7, + ReverseChainSingle = 8 + }; + + template + typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type, Ts&&... ds) const + { + TRACE_DISPATCH (this, lookup_type); + switch (lookup_type) { + case Single: return_trace (u.single.dispatch (c, std::forward (ds)...)); + case Multiple: return_trace (u.multiple.dispatch (c, std::forward (ds)...)); + case Alternate: return_trace (u.alternate.dispatch (c, std::forward (ds)...)); + case Ligature: return_trace (u.ligature.dispatch (c, std::forward (ds)...)); + case Context: return_trace (u.context.dispatch (c, std::forward (ds)...)); + case ChainContext: return_trace (u.chainContext.dispatch (c, std::forward (ds)...)); + case Extension: return_trace (u.extension.dispatch (c, std::forward (ds)...)); + case ReverseChainSingle: return_trace (u.reverseChainContextSingle.dispatch (c, std::forward (ds)...)); + default: return_trace (c->default_return_value ()); + } + } + + bool intersects (const hb_set_t *glyphs, unsigned int lookup_type) const + { + hb_intersects_context_t c (glyphs); + return dispatch (&c, lookup_type); + } +}; + + +} +} +} + +#endif /* HB_OT_LAYOUT_GSUB_SUBSTLOOKUPSUBTABLE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/Layout/types.hh b/src/3rdparty/harfbuzz-ng/src/OT/Layout/types.hh new file mode 100644 index 00000000000..6a43403e94b --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/Layout/types.hh @@ -0,0 +1,66 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2010,2012 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod, Garret Rieger + */ + +#ifndef OT_LAYOUT_TYPES_HH +#define OT_LAYOUT_TYPES_HH + +namespace OT { +namespace Layout { + +struct SmallTypes { + static constexpr unsigned size = 2; + using large_int = uint32_t; + using HBUINT = HBUINT16; + using HBGlyphID = HBGlyphID16; + using Offset = Offset16; + template + using OffsetTo = OT::Offset16To; + template + using ArrayOf = OT::Array16Of; + template + using SortedArrayOf = OT::SortedArray16Of; +}; + +struct MediumTypes { + static constexpr unsigned size = 3; + using large_int = uint64_t; + using HBUINT = HBUINT24; + using HBGlyphID = HBGlyphID24; + using Offset = Offset24; + template + using OffsetTo = OT::Offset24To; + template + using ArrayOf = OT::Array24Of; + template + using SortedArrayOf = OT::SortedArray24Of; +}; + +} +} + +#endif /* OT_LAYOUT_TYPES_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/glyf/CompositeGlyph.hh b/src/3rdparty/harfbuzz-ng/src/OT/glyf/CompositeGlyph.hh new file mode 100644 index 00000000000..60858a5a581 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/glyf/CompositeGlyph.hh @@ -0,0 +1,434 @@ +#ifndef OT_GLYF_COMPOSITEGLYPH_HH +#define OT_GLYF_COMPOSITEGLYPH_HH + + +#include "../../hb-open-type.hh" +#include "composite-iter.hh" + + +namespace OT { +namespace glyf_impl { + + +struct CompositeGlyphRecord +{ + protected: + enum composite_glyph_flag_t + { + ARG_1_AND_2_ARE_WORDS = 0x0001, + ARGS_ARE_XY_VALUES = 0x0002, + ROUND_XY_TO_GRID = 0x0004, + WE_HAVE_A_SCALE = 0x0008, + MORE_COMPONENTS = 0x0020, + WE_HAVE_AN_X_AND_Y_SCALE = 0x0040, + WE_HAVE_A_TWO_BY_TWO = 0x0080, + WE_HAVE_INSTRUCTIONS = 0x0100, + USE_MY_METRICS = 0x0200, + OVERLAP_COMPOUND = 0x0400, + SCALED_COMPONENT_OFFSET = 0x0800, + UNSCALED_COMPONENT_OFFSET = 0x1000, +#ifndef HB_NO_BEYOND_64K + GID_IS_24BIT = 0x2000 +#endif + }; + + public: + unsigned int get_size () const + { + unsigned int size = min_size; + /* glyphIndex is 24bit instead of 16bit */ +#ifndef HB_NO_BEYOND_64K + if (flags & GID_IS_24BIT) size += HBGlyphID24::static_size - HBGlyphID16::static_size; +#endif + /* arg1 and 2 are int16 */ + if (flags & ARG_1_AND_2_ARE_WORDS) size += 4; + /* arg1 and 2 are int8 */ + else size += 2; + + /* One x 16 bit (scale) */ + if (flags & WE_HAVE_A_SCALE) size += 2; + /* Two x 16 bit (xscale, yscale) */ + else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) size += 4; + /* Four x 16 bit (xscale, scale01, scale10, yscale) */ + else if (flags & WE_HAVE_A_TWO_BY_TWO) size += 8; + + return size; + } + + void drop_instructions_flag () { flags = (uint16_t) flags & ~WE_HAVE_INSTRUCTIONS; } + void set_overlaps_flag () + { + flags = (uint16_t) flags | OVERLAP_COMPOUND; + } + + bool has_instructions () const { return flags & WE_HAVE_INSTRUCTIONS; } + + bool has_more () const { return flags & MORE_COMPONENTS; } + bool is_use_my_metrics () const { return flags & USE_MY_METRICS; } + bool is_anchored () const { return !(flags & ARGS_ARE_XY_VALUES); } + void get_anchor_points (unsigned int &point1, unsigned int &point2) const + { + const auto *p = &StructAfter (flags); +#ifndef HB_NO_BEYOND_64K + if (flags & GID_IS_24BIT) + p += HBGlyphID24::static_size; + else +#endif + p += HBGlyphID16::static_size; + if (flags & ARG_1_AND_2_ARE_WORDS) + { + point1 = ((const HBUINT16 *) p)[0]; + point2 = ((const HBUINT16 *) p)[1]; + } + else + { + point1 = p[0]; + point2 = p[1]; + } + } + + static void transform (const float (&matrix)[4], + hb_array_t points) + { + if (matrix[0] != 1.f || matrix[1] != 0.f || + matrix[2] != 0.f || matrix[3] != 1.f) + for (auto &point : points) + point.transform (matrix); + } + + static void translate (const contour_point_t &trans, + hb_array_t points) + { + if (HB_OPTIMIZE_SIZE_VAL) + { + if (trans.x != 0.f || trans.y != 0.f) + for (auto &point : points) + point.translate (trans); + } + else + { + if (trans.x != 0.f && trans.y != 0.f) + for (auto &point : points) + point.translate (trans); + else + { + if (trans.x != 0.f) + for (auto &point : points) + point.x += trans.x; + else if (trans.y != 0.f) + for (auto &point : points) + point.y += trans.y; + } + } + } + + void transform_points (hb_array_t points, + const float (&matrix)[4], + const contour_point_t &trans) const + { + if (scaled_offsets ()) + { + translate (trans, points); + transform (matrix, points); + } + else + { + transform (matrix, points); + translate (trans, points); + } + } + + bool get_points (contour_point_vector_t &points) const + { + float matrix[4]; + contour_point_t trans; + get_transformation (matrix, trans); + if (unlikely (!points.alloc (points.length + 4))) return false; // For phantom points + points.push (trans); + return true; + } + + unsigned compile_with_point (const contour_point_t &point, + char *out) const + { + const HBINT8 *p = &StructAfter (flags); +#ifndef HB_NO_BEYOND_64K + if (flags & GID_IS_24BIT) + p += HBGlyphID24::static_size; + else +#endif + p += HBGlyphID16::static_size; + + unsigned len = get_size (); + unsigned len_before_val = (const char *)p - (const char *)this; + if (flags & ARG_1_AND_2_ARE_WORDS) + { + // no overflow, copy value + hb_memcpy (out, this, len); + + HBINT16 *o = reinterpret_cast (out + len_before_val); + o[0] = roundf (point.x); + o[1] = roundf (point.y); + } + else + { + int new_x = roundf (point.x); + int new_y = roundf (point.y); + if (new_x <= 127 && new_x >= -128 && + new_y <= 127 && new_y >= -128) + { + hb_memcpy (out, this, len); + HBINT8 *o = reinterpret_cast (out + len_before_val); + o[0] = new_x; + o[1] = new_y; + } + else + { + // new point value has an int8 overflow + hb_memcpy (out, this, len_before_val); + + //update flags + CompositeGlyphRecord *o = reinterpret_cast (out); + o->flags = flags | ARG_1_AND_2_ARE_WORDS; + out += len_before_val; + + HBINT16 new_value; + new_value = new_x; + hb_memcpy (out, &new_value, HBINT16::static_size); + out += HBINT16::static_size; + + new_value = new_y; + hb_memcpy (out, &new_value, HBINT16::static_size); + out += HBINT16::static_size; + + hb_memcpy (out, p+2, len - len_before_val - 2); + len += 2; + } + } + return len; + } + + protected: + bool scaled_offsets () const + { return (flags & (SCALED_COMPONENT_OFFSET | UNSCALED_COMPONENT_OFFSET)) == SCALED_COMPONENT_OFFSET; } + + public: + bool get_transformation (float (&matrix)[4], contour_point_t &trans) const + { + matrix[0] = matrix[3] = 1.f; + matrix[1] = matrix[2] = 0.f; + + const auto *p = &StructAfter (flags); +#ifndef HB_NO_BEYOND_64K + if (flags & GID_IS_24BIT) + p += HBGlyphID24::static_size; + else +#endif + p += HBGlyphID16::static_size; + int tx, ty; + if (flags & ARG_1_AND_2_ARE_WORDS) + { + tx = *(const HBINT16 *) p; + p += HBINT16::static_size; + ty = *(const HBINT16 *) p; + p += HBINT16::static_size; + } + else + { + tx = *p++; + ty = *p++; + } + if (is_anchored ()) tx = ty = 0; + + trans.init ((float) tx, (float) ty); + + { + const F2DOT14 *points = (const F2DOT14 *) p; + if (flags & WE_HAVE_A_SCALE) + { + matrix[0] = matrix[3] = points[0].to_float (); + return true; + } + else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) + { + matrix[0] = points[0].to_float (); + matrix[3] = points[1].to_float (); + return true; + } + else if (flags & WE_HAVE_A_TWO_BY_TWO) + { + matrix[0] = points[0].to_float (); + matrix[1] = points[1].to_float (); + matrix[2] = points[2].to_float (); + matrix[3] = points[3].to_float (); + return true; + } + } + return tx || ty; + } + + hb_codepoint_t get_gid () const + { +#ifndef HB_NO_BEYOND_64K + if (flags & GID_IS_24BIT) + return StructAfter (flags); + else +#endif + return StructAfter (flags); + } + void set_gid (hb_codepoint_t gid) + { +#ifndef HB_NO_BEYOND_64K + if (flags & GID_IS_24BIT) + StructAfter (flags) = gid; + else +#endif + /* TODO assert? */ + StructAfter (flags) = gid; + } + +#ifndef HB_NO_BEYOND_64K + void lower_gid_24_to_16 () + { + hb_codepoint_t gid = get_gid (); + if (!(flags & GID_IS_24BIT) || gid > 0xFFFFu) + return; + + /* Lower the flag and move the rest of the struct down. */ + + unsigned size = get_size (); + char *end = (char *) this + size; + char *p = &StructAfter (flags); + p += HBGlyphID24::static_size; + + flags = flags & ~GID_IS_24BIT; + set_gid (gid); + + memmove (p - HBGlyphID24::static_size + HBGlyphID16::static_size, p, end - p); + } +#endif + + protected: + HBUINT16 flags; + HBUINT24 pad; + public: + DEFINE_SIZE_MIN (4); +}; + +using composite_iter_t = composite_iter_tmpl; + +struct CompositeGlyph +{ + const GlyphHeader &header; + hb_bytes_t bytes; + CompositeGlyph (const GlyphHeader &header_, hb_bytes_t bytes_) : + header (header_), bytes (bytes_) {} + + composite_iter_t iter () const + { return composite_iter_t (bytes, &StructAfter (header)); } + + unsigned int instructions_length (hb_bytes_t bytes) const + { + unsigned int start = bytes.length; + unsigned int end = bytes.length; + const CompositeGlyphRecord *last = nullptr; + for (auto &item : iter ()) + last = &item; + if (unlikely (!last)) return 0; + + if (last->has_instructions ()) + start = (char *) last - &bytes + last->get_size (); + if (unlikely (start > end)) return 0; + return end - start; + } + + /* Trimming for composites not implemented. + * If removing hints it falls out of that. */ + const hb_bytes_t trim_padding () const { return bytes; } + + void drop_hints () + { + for (const auto &_ : iter ()) + const_cast (_).drop_instructions_flag (); + } + + /* Chop instructions off the end */ + void drop_hints_bytes (hb_bytes_t &dest_start) const + { dest_start = bytes.sub_array (0, bytes.length - instructions_length (bytes)); } + + void set_overlaps_flag () + { + CompositeGlyphRecord& glyph_chain = const_cast ( + StructAfter (header)); + if (!bytes.check_range(&glyph_chain, CompositeGlyphRecord::min_size)) + return; + glyph_chain.set_overlaps_flag (); + } + + bool compile_bytes_with_deltas (const hb_bytes_t &source_bytes, + const contour_point_vector_t &points_with_deltas, + hb_bytes_t &dest_bytes /* OUT */) + { + if (source_bytes.length <= GlyphHeader::static_size || + header.numberOfContours != -1) + { + dest_bytes = hb_bytes_t (); + return true; + } + + unsigned source_len = source_bytes.length - GlyphHeader::static_size; + + /* try to allocate more memories than source glyph bytes + * in case that there might be an overflow for int8 value + * and we would need to use int16 instead */ + char *o = (char *) hb_calloc (source_len * 2, sizeof (char)); + if (unlikely (!o)) return false; + + const CompositeGlyphRecord *c = reinterpret_cast (source_bytes.arrayZ + GlyphHeader::static_size); + auto it = composite_iter_t (hb_bytes_t ((const char *)c, source_len), c); + + char *p = o; + unsigned i = 0, source_comp_len = 0; + for (const auto &component : it) + { + /* last 4 points in points_with_deltas are phantom points and should not be included */ + if (i >= points_with_deltas.length - 4) { + hb_free (o); + return false; + } + + unsigned comp_len = component.get_size (); + if (component.is_anchored ()) + { + hb_memcpy (p, &component, comp_len); + p += comp_len; + } + else + { + unsigned new_len = component.compile_with_point (points_with_deltas[i], p); + p += new_len; + } + i++; + source_comp_len += comp_len; + } + + //copy instructions if any + if (source_len > source_comp_len) + { + unsigned instr_len = source_len - source_comp_len; + hb_memcpy (p, (const char *)c + source_comp_len, instr_len); + p += instr_len; + } + + unsigned len = p - o; + dest_bytes = hb_bytes_t (o, len); + return true; + } +}; + + +} /* namespace glyf_impl */ +} /* namespace OT */ + + +#endif /* OT_GLYF_COMPOSITEGLYPH_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/glyf/Glyph.hh b/src/3rdparty/harfbuzz-ng/src/OT/glyf/Glyph.hh new file mode 100644 index 00000000000..5ea611948f8 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/glyf/Glyph.hh @@ -0,0 +1,680 @@ +#ifndef OT_GLYF_GLYPH_HH +#define OT_GLYF_GLYPH_HH + + +#include "../../hb-open-type.hh" + +#include "GlyphHeader.hh" +#include "SimpleGlyph.hh" +#include "CompositeGlyph.hh" +#include "VarCompositeGlyph.hh" +#include "coord-setter.hh" + + +namespace OT { + +struct glyf_accelerator_t; + +namespace glyf_impl { + + +enum phantom_point_index_t +{ + PHANTOM_LEFT = 0, + PHANTOM_RIGHT = 1, + PHANTOM_TOP = 2, + PHANTOM_BOTTOM = 3, + PHANTOM_COUNT = 4 +}; + +struct Glyph +{ + enum glyph_type_t { + EMPTY, + SIMPLE, + COMPOSITE, +#ifndef HB_NO_VAR_COMPOSITES + VAR_COMPOSITE, +#endif + }; + + public: + composite_iter_t get_composite_iterator () const + { + if (type != COMPOSITE) return composite_iter_t (); + return CompositeGlyph (*header, bytes).iter (); + } + var_composite_iter_t get_var_composite_iterator () const + { +#ifndef HB_NO_VAR_COMPOSITES + if (type != VAR_COMPOSITE) return var_composite_iter_t (); + return VarCompositeGlyph (*header, bytes).iter (); +#else + return var_composite_iter_t (); +#endif + } + + const hb_bytes_t trim_padding () const + { + switch (type) { +#ifndef HB_NO_VAR_COMPOSITES + case VAR_COMPOSITE: return VarCompositeGlyph (*header, bytes).trim_padding (); +#endif + case COMPOSITE: return CompositeGlyph (*header, bytes).trim_padding (); + case SIMPLE: return SimpleGlyph (*header, bytes).trim_padding (); + case EMPTY: return bytes; + default: return bytes; + } + } + + void drop_hints () + { + switch (type) { +#ifndef HB_NO_VAR_COMPOSITES + case VAR_COMPOSITE: return; // No hinting +#endif + case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints (); return; + case SIMPLE: SimpleGlyph (*header, bytes).drop_hints (); return; + case EMPTY: return; + } + } + + void set_overlaps_flag () + { + switch (type) { +#ifndef HB_NO_VAR_COMPOSITES + case VAR_COMPOSITE: return; // No overlaps flag +#endif + case COMPOSITE: CompositeGlyph (*header, bytes).set_overlaps_flag (); return; + case SIMPLE: SimpleGlyph (*header, bytes).set_overlaps_flag (); return; + case EMPTY: return; + } + } + + void drop_hints_bytes (hb_bytes_t &dest_start, hb_bytes_t &dest_end) const + { + switch (type) { +#ifndef HB_NO_VAR_COMPOSITES + case VAR_COMPOSITE: return; // No hinting +#endif + case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints_bytes (dest_start); return; + case SIMPLE: SimpleGlyph (*header, bytes).drop_hints_bytes (dest_start, dest_end); return; + case EMPTY: return; + } + } + + bool get_all_points_without_var (const hb_face_t *face, + contour_point_vector_t &points /* OUT */) const + { + switch (type) { + case SIMPLE: + if (unlikely (!SimpleGlyph (*header, bytes).get_contour_points (points))) + return false; + break; + case COMPOSITE: + { + for (auto &item : get_composite_iterator ()) + if (unlikely (!item.get_points (points))) return false; + break; + } +#ifndef HB_NO_VAR_COMPOSITES + case VAR_COMPOSITE: + { + for (auto &item : get_var_composite_iterator ()) + if (unlikely (!item.get_points (points))) return false; + break; + } +#endif + case EMPTY: + break; + } + + /* Init phantom points */ + if (unlikely (!points.resize (points.length + PHANTOM_COUNT))) return false; + hb_array_t phantoms = points.as_array ().sub_array (points.length - PHANTOM_COUNT, PHANTOM_COUNT); + { + int lsb = 0; + int h_delta = face->table.hmtx->get_leading_bearing_without_var_unscaled (gid, &lsb) ? + (int) header->xMin - lsb : 0; + HB_UNUSED int tsb = 0; + int v_orig = (int) header->yMax + +#ifndef HB_NO_VERTICAL + ((void) face->table.vmtx->get_leading_bearing_without_var_unscaled (gid, &tsb), tsb) +#else + 0 +#endif + ; + unsigned h_adv = face->table.hmtx->get_advance_without_var_unscaled (gid); + unsigned v_adv = +#ifndef HB_NO_VERTICAL + face->table.vmtx->get_advance_without_var_unscaled (gid) +#else + - face->get_upem () +#endif + ; + phantoms[PHANTOM_LEFT].x = h_delta; + phantoms[PHANTOM_RIGHT].x = (int) h_adv + h_delta; + phantoms[PHANTOM_TOP].y = v_orig; + phantoms[PHANTOM_BOTTOM].y = v_orig - (int) v_adv; + } + return true; + } + + void update_mtx (const hb_subset_plan_t *plan, + int xMin, int xMax, + int yMin, int yMax, + const contour_point_vector_t &all_points) const + { + hb_codepoint_t new_gid = 0; + if (!plan->new_gid_for_old_gid (gid, &new_gid)) + return; + + if (type != EMPTY) + { + plan->bounds_width_vec[new_gid] = xMax - xMin; + plan->bounds_height_vec[new_gid] = yMax - yMin; + } + + unsigned len = all_points.length; + float leftSideX = all_points[len - 4].x; + float rightSideX = all_points[len - 3].x; + float topSideY = all_points[len - 2].y; + float bottomSideY = all_points[len - 1].y; + + uint32_t hash = hb_hash (new_gid); + + signed hori_aw = roundf (rightSideX - leftSideX); + if (hori_aw < 0) hori_aw = 0; + int lsb = roundf (xMin - leftSideX); + plan->hmtx_map.set_with_hash (new_gid, hash, hb_pair ((unsigned) hori_aw, lsb)); + //flag value should be computed using non-empty glyphs + if (type != EMPTY && lsb != xMin) + plan->head_maxp_info.allXMinIsLsb = false; + + signed vert_aw = roundf (topSideY - bottomSideY); + if (vert_aw < 0) vert_aw = 0; + int tsb = roundf (topSideY - yMax); + plan->vmtx_map.set_with_hash (new_gid, hash, hb_pair ((unsigned) vert_aw, tsb)); + } + + bool compile_header_bytes (const hb_subset_plan_t *plan, + const contour_point_vector_t &all_points, + hb_bytes_t &dest_bytes /* OUT */) const + { + GlyphHeader *glyph_header = nullptr; + if (!plan->pinned_at_default && type != EMPTY && all_points.length >= 4) + { + glyph_header = (GlyphHeader *) hb_calloc (1, GlyphHeader::static_size); + if (unlikely (!glyph_header)) return false; + } + + float xMin = 0, xMax = 0; + float yMin = 0, yMax = 0; + if (all_points.length > 4) + { + xMin = xMax = all_points[0].x; + yMin = yMax = all_points[0].y; + + unsigned count = all_points.length - 4; + for (unsigned i = 1; i < count; i++) + { + float x = all_points[i].x; + float y = all_points[i].y; + xMin = hb_min (xMin, x); + xMax = hb_max (xMax, x); + yMin = hb_min (yMin, y); + yMax = hb_max (yMax, y); + } + } + + + // These are destined for storage in a 16 bit field to clamp the values to + // fit into a 16 bit signed integer. + int rounded_xMin = hb_clamp (roundf (xMin), -32768.0f, 32767.0f); + int rounded_xMax = hb_clamp (roundf (xMax), -32768.0f, 32767.0f); + int rounded_yMin = hb_clamp (roundf (yMin), -32768.0f, 32767.0f); + int rounded_yMax = hb_clamp (roundf (yMax), -32768.0f, 32767.0f); + + update_mtx (plan, rounded_xMin, rounded_xMax, rounded_yMin, rounded_yMax, all_points); + + if (type != EMPTY) + { + plan->head_maxp_info.xMin = hb_min (plan->head_maxp_info.xMin, rounded_xMin); + plan->head_maxp_info.yMin = hb_min (plan->head_maxp_info.yMin, rounded_yMin); + plan->head_maxp_info.xMax = hb_max (plan->head_maxp_info.xMax, rounded_xMax); + plan->head_maxp_info.yMax = hb_max (plan->head_maxp_info.yMax, rounded_yMax); + } + + /* when pinned at default, no need to compile glyph header + * and for empty glyphs: all_points only include phantom points. + * just update metrics and then return */ + if (!glyph_header) + return true; + + glyph_header->numberOfContours = header->numberOfContours; + + glyph_header->xMin = rounded_xMin; + glyph_header->yMin = rounded_yMin; + glyph_header->xMax = rounded_xMax; + glyph_header->yMax = rounded_yMax; + + dest_bytes = hb_bytes_t ((const char *)glyph_header, GlyphHeader::static_size); + return true; + } + + bool compile_bytes_with_deltas (const hb_subset_plan_t *plan, + hb_font_t *font, + const glyf_accelerator_t &glyf, + hb_bytes_t &dest_start, /* IN/OUT */ + hb_bytes_t &dest_end /* OUT */) + { + contour_point_vector_t all_points, points_with_deltas; + unsigned composite_contours = 0; + head_maxp_info_t *head_maxp_info_p = &plan->head_maxp_info; + unsigned *composite_contours_p = &composite_contours; + + // don't compute head/maxp values when glyph has no contours(type is EMPTY) + // also ignore .notdef glyph when --notdef-outline is not enabled + if (type == EMPTY || + (gid == 0 && !(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE))) + { + head_maxp_info_p = nullptr; + composite_contours_p = nullptr; + } + + if (!get_points (font, glyf, all_points, &points_with_deltas, head_maxp_info_p, composite_contours_p, false, false)) + return false; + + // .notdef, set type to empty so we only update metrics and don't compile bytes for + // it + if (gid == 0 && + !(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE)) + { + type = EMPTY; + dest_start = hb_bytes_t (); + dest_end = hb_bytes_t (); + } + + //dont compile bytes when pinned at default, just recalculate bounds + if (!plan->pinned_at_default) + { + switch (type) + { +#ifndef HB_NO_VAR_COMPOSITES + case VAR_COMPOSITE: + // TODO + dest_end = hb_bytes_t (); + break; +#endif + + case COMPOSITE: + if (!CompositeGlyph (*header, bytes).compile_bytes_with_deltas (dest_start, + points_with_deltas, + dest_end)) + return false; + break; + case SIMPLE: + if (!SimpleGlyph (*header, bytes).compile_bytes_with_deltas (all_points, + plan->flags & HB_SUBSET_FLAGS_NO_HINTING, + dest_end)) + return false; + break; + case EMPTY: + /* set empty bytes for empty glyph + * do not use source glyph's pointers */ + dest_start = hb_bytes_t (); + dest_end = hb_bytes_t (); + break; + } + } + + if (!compile_header_bytes (plan, all_points, dest_start)) + { + dest_end.fini (); + return false; + } + return true; + } + + + /* Note: Recursively calls itself. + * all_points includes phantom points + */ + template + bool get_points (hb_font_t *font, const accelerator_t &glyf_accelerator, + contour_point_vector_t &all_points /* OUT */, + contour_point_vector_t *points_with_deltas = nullptr, /* OUT */ + head_maxp_info_t * head_maxp_info = nullptr, /* OUT */ + unsigned *composite_contours = nullptr, /* OUT */ + bool shift_points_hori = true, + bool use_my_metrics = true, + bool phantom_only = false, + hb_array_t coords = hb_array_t (), + hb_map_t *current_glyphs = nullptr, + unsigned int depth = 0, + unsigned *edge_count = nullptr) const + { + if (unlikely (depth > HB_MAX_NESTING_LEVEL)) return false; + unsigned stack_edge_count = 0; + if (!edge_count) edge_count = &stack_edge_count; + if (unlikely (*edge_count > HB_GLYF_MAX_EDGE_COUNT)) return false; + (*edge_count)++; + + hb_map_t current_glyphs_stack; + if (current_glyphs == nullptr) + current_glyphs = ¤t_glyphs_stack; + + if (head_maxp_info) + { + head_maxp_info->maxComponentDepth = hb_max (head_maxp_info->maxComponentDepth, depth); + } + + if (!coords) + coords = hb_array (font->coords, font->num_coords); + + contour_point_vector_t stack_points; + contour_point_vector_t &points = type == SIMPLE ? all_points : stack_points; + unsigned old_length = points.length; + + switch (type) { + case SIMPLE: + if (depth == 0 && head_maxp_info) + head_maxp_info->maxContours = hb_max (head_maxp_info->maxContours, (unsigned) header->numberOfContours); + if (depth > 0 && composite_contours) + *composite_contours += (unsigned) header->numberOfContours; + if (unlikely (!SimpleGlyph (*header, bytes).get_contour_points (all_points, phantom_only))) + return false; + break; + case COMPOSITE: + { + for (auto &item : get_composite_iterator ()) + if (unlikely (!item.get_points (points))) return false; + break; + } +#ifndef HB_NO_VAR_COMPOSITES + case VAR_COMPOSITE: + { + for (auto &item : get_var_composite_iterator ()) + if (unlikely (!item.get_points (points))) return false; + break; + } +#endif + case EMPTY: + break; + } + + /* Init phantom points */ + if (unlikely (!points.resize (points.length + PHANTOM_COUNT))) return false; + hb_array_t phantoms = points.as_array ().sub_array (points.length - PHANTOM_COUNT, PHANTOM_COUNT); + { + int lsb = 0; + int h_delta = glyf_accelerator.hmtx->get_leading_bearing_without_var_unscaled (gid, &lsb) ? + (int) header->xMin - lsb : 0; + HB_UNUSED int tsb = 0; + int v_orig = (int) header->yMax + +#ifndef HB_NO_VERTICAL + ((void) glyf_accelerator.vmtx->get_leading_bearing_without_var_unscaled (gid, &tsb), tsb) +#else + 0 +#endif + ; + unsigned h_adv = glyf_accelerator.hmtx->get_advance_without_var_unscaled (gid); + unsigned v_adv = +#ifndef HB_NO_VERTICAL + glyf_accelerator.vmtx->get_advance_without_var_unscaled (gid) +#else + - font->face->get_upem () +#endif + ; + phantoms[PHANTOM_LEFT].x = h_delta; + phantoms[PHANTOM_RIGHT].x = (int) h_adv + h_delta; + phantoms[PHANTOM_TOP].y = v_orig; + phantoms[PHANTOM_BOTTOM].y = v_orig - (int) v_adv; + } + +#ifndef HB_NO_VAR + if (coords) + glyf_accelerator.gvar->apply_deltas_to_points (gid, + coords, + points.as_array ().sub_array (old_length), + phantom_only && type == SIMPLE); +#endif + + // mainly used by CompositeGlyph calculating new X/Y offset value so no need to extend it + // with child glyphs' points + if (points_with_deltas != nullptr && depth == 0 && type == COMPOSITE) + { + if (unlikely (!points_with_deltas->resize (points.length))) return false; + *points_with_deltas = points; + } + + switch (type) { + case SIMPLE: + if (depth == 0 && head_maxp_info) + head_maxp_info->maxPoints = hb_max (head_maxp_info->maxPoints, all_points.length - old_length - 4); + break; + case COMPOSITE: + { + unsigned int comp_index = 0; + for (auto &item : get_composite_iterator ()) + { + hb_codepoint_t item_gid = item.get_gid (); + + if (unlikely (current_glyphs->has (item_gid))) + continue; + + current_glyphs->add (item_gid); + + unsigned old_count = all_points.length; + + if (unlikely ((!phantom_only || (use_my_metrics && item.is_use_my_metrics ())) && + !glyf_accelerator.glyph_for_gid (item_gid) + .get_points (font, + glyf_accelerator, + all_points, + points_with_deltas, + head_maxp_info, + composite_contours, + shift_points_hori, + use_my_metrics, + phantom_only, + coords, + current_glyphs, + depth + 1, + edge_count))) + { + current_glyphs->del (item_gid); + return false; + } + + auto comp_points = all_points.as_array ().sub_array (old_count); + + /* Copy phantom points from component if USE_MY_METRICS flag set */ + if (use_my_metrics && item.is_use_my_metrics ()) + for (unsigned int i = 0; i < PHANTOM_COUNT; i++) + phantoms[i] = comp_points[comp_points.length - PHANTOM_COUNT + i]; + + if (comp_points) // Empty in case of phantom_only + { + float matrix[4]; + contour_point_t default_trans; + item.get_transformation (matrix, default_trans); + + /* Apply component transformation & translation (with deltas applied) */ + item.transform_points (comp_points, matrix, points[comp_index]); + } + + if (item.is_anchored () && !phantom_only) + { + unsigned int p1, p2; + item.get_anchor_points (p1, p2); + if (likely (p1 < all_points.length && p2 < comp_points.length)) + { + contour_point_t delta; + delta.init (all_points[p1].x - comp_points[p2].x, + all_points[p1].y - comp_points[p2].y); + + item.translate (delta, comp_points); + } + } + + all_points.resize (all_points.length - PHANTOM_COUNT); + + if (all_points.length > HB_GLYF_MAX_POINTS) + { + current_glyphs->del (item_gid); + return false; + } + + comp_index++; + current_glyphs->del (item_gid); + } + + if (head_maxp_info && depth == 0) + { + if (composite_contours) + head_maxp_info->maxCompositeContours = hb_max (head_maxp_info->maxCompositeContours, *composite_contours); + head_maxp_info->maxCompositePoints = hb_max (head_maxp_info->maxCompositePoints, all_points.length); + head_maxp_info->maxComponentElements = hb_max (head_maxp_info->maxComponentElements, comp_index); + } + all_points.extend (phantoms); + } break; +#ifndef HB_NO_VAR_COMPOSITES + case VAR_COMPOSITE: + { + hb_array_t points_left = points.as_array (); + for (auto &item : get_var_composite_iterator ()) + { + hb_codepoint_t item_gid = item.get_gid (); + + if (unlikely (current_glyphs->has (item_gid))) + continue; + + current_glyphs->add (item_gid); + + unsigned item_num_points = item.get_num_points (); + hb_array_t record_points = points_left.sub_array (0, item_num_points); + assert (record_points.length == item_num_points); + + auto component_coords = coords; + /* Copying coords is expensive; so we have put an arbitrary + * limit on the max number of coords for now. */ + if (item.is_reset_unspecified_axes () || + coords.length > HB_GLYF_VAR_COMPOSITE_MAX_AXES) + component_coords = hb_array (); + + coord_setter_t coord_setter (component_coords); + item.set_variations (coord_setter, record_points); + + unsigned old_count = all_points.length; + + if (unlikely ((!phantom_only || (use_my_metrics && item.is_use_my_metrics ())) && + !glyf_accelerator.glyph_for_gid (item_gid) + .get_points (font, + glyf_accelerator, + all_points, + points_with_deltas, + head_maxp_info, + nullptr, + shift_points_hori, + use_my_metrics, + phantom_only, + coord_setter.get_coords (), + current_glyphs, + depth + 1, + edge_count))) + { + current_glyphs->del (item_gid); + return false; + } + + auto comp_points = all_points.as_array ().sub_array (old_count); + + /* Apply component transformation */ + if (comp_points) // Empty in case of phantom_only + item.transform_points (record_points, comp_points); + + /* Copy phantom points from component if USE_MY_METRICS flag set */ + if (use_my_metrics && item.is_use_my_metrics ()) + for (unsigned int i = 0; i < PHANTOM_COUNT; i++) + phantoms[i] = comp_points[comp_points.length - PHANTOM_COUNT + i]; + + all_points.resize (all_points.length - PHANTOM_COUNT); + + if (all_points.length > HB_GLYF_MAX_POINTS) + { + current_glyphs->del (item_gid); + return false; + } + + points_left += item_num_points; + + current_glyphs->del (item_gid); + } + all_points.extend (phantoms); + } break; +#endif + case EMPTY: + all_points.extend (phantoms); + break; + } + + if (depth == 0 && shift_points_hori) /* Apply at top level */ + { + /* Undocumented rasterizer behavior: + * Shift points horizontally by the updated left side bearing + */ + int v = -phantoms[PHANTOM_LEFT].x; + if (v) + for (auto &point : all_points) + point.x += v; + } + + return !all_points.in_error (); + } + + bool get_extents_without_var_scaled (hb_font_t *font, const glyf_accelerator_t &glyf_accelerator, + hb_glyph_extents_t *extents) const + { + if (type == EMPTY) return true; /* Empty glyph; zero extents. */ + return header->get_extents_without_var_scaled (font, glyf_accelerator, gid, extents); + } + + hb_bytes_t get_bytes () const { return bytes; } + glyph_type_t get_type () const { return type; } + const GlyphHeader *get_header () const { return header; } + + Glyph () : bytes (), + header (bytes.as ()), + gid (-1), + type(EMPTY) + {} + + Glyph (hb_bytes_t bytes_, + hb_codepoint_t gid_ = (unsigned) -1) : bytes (bytes_), + header (bytes.as ()), + gid (gid_) + { + int num_contours = header->numberOfContours; + if (unlikely (num_contours == 0)) type = EMPTY; + else if (num_contours > 0) type = SIMPLE; + else if (num_contours == -1) type = COMPOSITE; +#ifndef HB_NO_VAR_COMPOSITES + else if (num_contours == -2) type = VAR_COMPOSITE; +#endif + else type = EMPTY; // Spec deviation; Spec says COMPOSITE, but not seen in the wild. + } + + protected: + hb_bytes_t bytes; + const GlyphHeader *header; + hb_codepoint_t gid; + glyph_type_t type; +}; + + +} /* namespace glyf_impl */ +} /* namespace OT */ + + +#endif /* OT_GLYF_GLYPH_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/glyf/GlyphHeader.hh b/src/3rdparty/harfbuzz-ng/src/OT/glyf/GlyphHeader.hh new file mode 100644 index 00000000000..a43b6691ab4 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/glyf/GlyphHeader.hh @@ -0,0 +1,52 @@ +#ifndef OT_GLYF_GLYPHHEADER_HH +#define OT_GLYF_GLYPHHEADER_HH + + +#include "../../hb-open-type.hh" + + +namespace OT { +namespace glyf_impl { + + +struct GlyphHeader +{ + bool has_data () const { return numberOfContours; } + + template + bool get_extents_without_var_scaled (hb_font_t *font, const accelerator_t &glyf_accelerator, + hb_codepoint_t gid, hb_glyph_extents_t *extents) const + { + /* Undocumented rasterizer behavior: shift glyph to the left by (lsb - xMin), i.e., xMin = lsb */ + /* extents->x_bearing = hb_min (glyph_header.xMin, glyph_header.xMax); */ + int lsb = hb_min (xMin, xMax); + (void) glyf_accelerator.hmtx->get_leading_bearing_without_var_unscaled (gid, &lsb); + extents->x_bearing = lsb; + extents->y_bearing = hb_max (yMin, yMax); + extents->width = hb_max (xMin, xMax) - hb_min (xMin, xMax); + extents->height = hb_min (yMin, yMax) - hb_max (yMin, yMax); + + font->scale_glyph_extents (extents); + + return true; + } + + HBINT16 numberOfContours; + /* If the number of contours is + * greater than or equal to zero, + * this is a simple glyph; if negative, + * this is a composite glyph. */ + FWORD xMin; /* Minimum x for coordinate data. */ + FWORD yMin; /* Minimum y for coordinate data. */ + FWORD xMax; /* Maximum x for coordinate data. */ + FWORD yMax; /* Maximum y for coordinate data. */ + public: + DEFINE_SIZE_STATIC (10); +}; + + +} /* namespace glyf_impl */ +} /* namespace OT */ + + +#endif /* OT_GLYF_GLYPHHEADER_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/glyf/SimpleGlyph.hh b/src/3rdparty/harfbuzz-ng/src/OT/glyf/SimpleGlyph.hh new file mode 100644 index 00000000000..1d42cc29250 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/glyf/SimpleGlyph.hh @@ -0,0 +1,348 @@ +#ifndef OT_GLYF_SIMPLEGLYPH_HH +#define OT_GLYF_SIMPLEGLYPH_HH + + +#include "../../hb-open-type.hh" + + +namespace OT { +namespace glyf_impl { + + +struct SimpleGlyph +{ + enum simple_glyph_flag_t + { + FLAG_ON_CURVE = 0x01, + FLAG_X_SHORT = 0x02, + FLAG_Y_SHORT = 0x04, + FLAG_REPEAT = 0x08, + FLAG_X_SAME = 0x10, + FLAG_Y_SAME = 0x20, + FLAG_OVERLAP_SIMPLE = 0x40, + FLAG_CUBIC = 0x80 + }; + + const GlyphHeader &header; + hb_bytes_t bytes; + SimpleGlyph (const GlyphHeader &header_, hb_bytes_t bytes_) : + header (header_), bytes (bytes_) {} + + unsigned int instruction_len_offset () const + { return GlyphHeader::static_size + 2 * header.numberOfContours; } + + unsigned int length (unsigned int instruction_len) const + { return instruction_len_offset () + 2 + instruction_len; } + + bool has_instructions_length () const + { + return instruction_len_offset () + 2 <= bytes.length; + } + + unsigned int instructions_length () const + { + unsigned int instruction_length_offset = instruction_len_offset (); + if (unlikely (instruction_length_offset + 2 > bytes.length)) return 0; + + const HBUINT16 &instructionLength = StructAtOffset (&bytes, instruction_length_offset); + /* Out of bounds of the current glyph */ + if (unlikely (length (instructionLength) > bytes.length)) return 0; + return instructionLength; + } + + const hb_bytes_t trim_padding () const + { + /* based on FontTools _g_l_y_f.py::trim */ + const uint8_t *glyph = (uint8_t*) bytes.arrayZ; + const uint8_t *glyph_end = glyph + bytes.length; + /* simple glyph w/contours, possibly trimmable */ + glyph += instruction_len_offset (); + + if (unlikely (glyph + 2 >= glyph_end)) return hb_bytes_t (); + unsigned int num_coordinates = StructAtOffset (glyph - 2, 0) + 1; + unsigned int num_instructions = StructAtOffset (glyph, 0); + + glyph += 2 + num_instructions; + + unsigned int coord_bytes = 0; + unsigned int coords_with_flags = 0; + while (glyph < glyph_end) + { + uint8_t flag = *glyph; + glyph++; + + unsigned int repeat = 1; + if (flag & FLAG_REPEAT) + { + if (unlikely (glyph >= glyph_end)) return hb_bytes_t (); + repeat = *glyph + 1; + glyph++; + } + + unsigned int xBytes, yBytes; + xBytes = yBytes = 0; + if (flag & FLAG_X_SHORT) xBytes = 1; + else if ((flag & FLAG_X_SAME) == 0) xBytes = 2; + + if (flag & FLAG_Y_SHORT) yBytes = 1; + else if ((flag & FLAG_Y_SAME) == 0) yBytes = 2; + + coord_bytes += (xBytes + yBytes) * repeat; + coords_with_flags += repeat; + if (coords_with_flags >= num_coordinates) break; + } + + if (unlikely (coords_with_flags != num_coordinates)) return hb_bytes_t (); + return bytes.sub_array (0, bytes.length + coord_bytes - (glyph_end - glyph)); + } + + /* zero instruction length */ + void drop_hints () + { + if (!has_instructions_length ()) return; + GlyphHeader &glyph_header = const_cast (header); + (HBUINT16 &) StructAtOffset (&glyph_header, instruction_len_offset ()) = 0; + } + + void drop_hints_bytes (hb_bytes_t &dest_start, hb_bytes_t &dest_end) const + { + unsigned int instructions_len = instructions_length (); + unsigned int glyph_length = length (instructions_len); + dest_start = bytes.sub_array (0, glyph_length - instructions_len); + dest_end = bytes.sub_array (glyph_length, bytes.length - glyph_length); + } + + void set_overlaps_flag () + { + if (unlikely (!header.numberOfContours)) return; + + unsigned flags_offset = length (instructions_length ()); + if (unlikely (flags_offset + 1 > bytes.length)) return; + + HBUINT8 &first_flag = (HBUINT8 &) StructAtOffset (&bytes, flags_offset); + first_flag = (uint8_t) first_flag | FLAG_OVERLAP_SIMPLE; + } + + static bool read_flags (const HBUINT8 *&p /* IN/OUT */, + hb_array_t points_ /* IN/OUT */, + const HBUINT8 *end) + { + unsigned count = points_.length; + for (unsigned int i = 0; i < count;) + { + if (unlikely (p + 1 > end)) return false; + uint8_t flag = *p++; + points_.arrayZ[i++].flag = flag; + if (flag & FLAG_REPEAT) + { + if (unlikely (p + 1 > end)) return false; + unsigned int repeat_count = *p++; + unsigned stop = hb_min (i + repeat_count, count); + for (; i < stop; i++) + points_.arrayZ[i].flag = flag; + } + } + return true; + } + + static bool read_points (const HBUINT8 *&p /* IN/OUT */, + hb_array_t points_ /* IN/OUT */, + const HBUINT8 *end, + float contour_point_t::*m, + const simple_glyph_flag_t short_flag, + const simple_glyph_flag_t same_flag) + { + int v = 0; + + for (auto &point : points_) + { + unsigned flag = point.flag; + if (flag & short_flag) + { + if (unlikely (p + 1 > end)) return false; + if (flag & same_flag) + v += *p++; + else + v -= *p++; + } + else + { + if (!(flag & same_flag)) + { + if (unlikely (p + HBINT16::static_size > end)) return false; + v += *(const HBINT16 *) p; + p += HBINT16::static_size; + } + } + point.*m = v; + } + return true; + } + + bool get_contour_points (contour_point_vector_t &points /* OUT */, + bool phantom_only = false) const + { + const HBUINT16 *endPtsOfContours = &StructAfter (header); + int num_contours = header.numberOfContours; + assert (num_contours > 0); + /* One extra item at the end, for the instruction-count below. */ + if (unlikely (!bytes.check_range (&endPtsOfContours[num_contours]))) return false; + unsigned int num_points = endPtsOfContours[num_contours - 1] + 1; + + unsigned old_length = points.length; + points.alloc (points.length + num_points + 4, true); // Allocate for phantom points, to avoid a possible copy + if (unlikely (!points.resize (points.length + num_points, false))) return false; + auto points_ = points.as_array ().sub_array (old_length); + if (!phantom_only) + hb_memset (points_.arrayZ, 0, sizeof (contour_point_t) * num_points); + if (phantom_only) return true; + + for (int i = 0; i < num_contours; i++) + points_[endPtsOfContours[i]].is_end_point = true; + + /* Skip instructions */ + const HBUINT8 *p = &StructAtOffset (&endPtsOfContours[num_contours + 1], + endPtsOfContours[num_contours]); + + if (unlikely ((const char *) p < bytes.arrayZ)) return false; /* Unlikely overflow */ + const HBUINT8 *end = (const HBUINT8 *) (bytes.arrayZ + bytes.length); + if (unlikely (p >= end)) return false; + + /* Read x & y coordinates */ + return read_flags (p, points_, end) + && read_points (p, points_, end, &contour_point_t::x, + FLAG_X_SHORT, FLAG_X_SAME) + && read_points (p, points_, end, &contour_point_t::y, + FLAG_Y_SHORT, FLAG_Y_SAME); + } + + static void encode_coord (int value, + unsigned &flag, + const simple_glyph_flag_t short_flag, + const simple_glyph_flag_t same_flag, + hb_vector_t &coords /* OUT */) + { + if (value == 0) + { + flag |= same_flag; + } + else if (value >= -255 && value <= 255) + { + flag |= short_flag; + if (value > 0) flag |= same_flag; + else value = -value; + + coords.arrayZ[coords.length++] = (uint8_t) value; + } + else + { + int16_t val = value; + coords.arrayZ[coords.length++] = val >> 8; + coords.arrayZ[coords.length++] = val & 0xff; + } + } + + static void encode_flag (unsigned flag, + unsigned &repeat, + unsigned lastflag, + hb_vector_t &flags /* OUT */) + { + if (flag == lastflag && repeat != 255) + { + repeat++; + if (repeat == 1) + { + /* We know there's room. */ + flags.arrayZ[flags.length++] = flag; + } + else + { + unsigned len = flags.length; + flags.arrayZ[len-2] = flag | FLAG_REPEAT; + flags.arrayZ[len-1] = repeat; + } + } + else + { + repeat = 0; + flags.arrayZ[flags.length++] = flag; + } + } + + bool compile_bytes_with_deltas (const contour_point_vector_t &all_points, + bool no_hinting, + hb_bytes_t &dest_bytes /* OUT */) + { + if (header.numberOfContours == 0 || all_points.length <= 4) + { + dest_bytes = hb_bytes_t (); + return true; + } + unsigned num_points = all_points.length - 4; + + hb_vector_t flags, x_coords, y_coords; + if (unlikely (!flags.alloc (num_points, true))) return false; + if (unlikely (!x_coords.alloc (2*num_points, true))) return false; + if (unlikely (!y_coords.alloc (2*num_points, true))) return false; + + unsigned lastflag = 255, repeat = 0; + int prev_x = 0, prev_y = 0; + + for (unsigned i = 0; i < num_points; i++) + { + unsigned flag = all_points.arrayZ[i].flag; + flag &= FLAG_ON_CURVE | FLAG_OVERLAP_SIMPLE | FLAG_CUBIC; + + int cur_x = roundf (all_points.arrayZ[i].x); + int cur_y = roundf (all_points.arrayZ[i].y); + encode_coord (cur_x - prev_x, flag, FLAG_X_SHORT, FLAG_X_SAME, x_coords); + encode_coord (cur_y - prev_y, flag, FLAG_Y_SHORT, FLAG_Y_SAME, y_coords); + encode_flag (flag, repeat, lastflag, flags); + + prev_x = cur_x; + prev_y = cur_y; + lastflag = flag; + } + + unsigned len_before_instrs = 2 * header.numberOfContours + 2; + unsigned len_instrs = instructions_length (); + unsigned total_len = len_before_instrs + flags.length + x_coords.length + y_coords.length; + + if (!no_hinting) + total_len += len_instrs; + + char *p = (char *) hb_malloc (total_len); + if (unlikely (!p)) return false; + + const char *src = bytes.arrayZ + GlyphHeader::static_size; + char *cur = p; + hb_memcpy (p, src, len_before_instrs); + + cur += len_before_instrs; + src += len_before_instrs; + + if (!no_hinting) + { + hb_memcpy (cur, src, len_instrs); + cur += len_instrs; + } + + hb_memcpy (cur, flags.arrayZ, flags.length); + cur += flags.length; + + hb_memcpy (cur, x_coords.arrayZ, x_coords.length); + cur += x_coords.length; + + hb_memcpy (cur, y_coords.arrayZ, y_coords.length); + + dest_bytes = hb_bytes_t (p, total_len); + return true; + } +}; + + +} /* namespace glyf_impl */ +} /* namespace OT */ + + +#endif /* OT_GLYF_SIMPLEGLYPH_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/glyf/SubsetGlyph.hh b/src/3rdparty/harfbuzz-ng/src/OT/glyf/SubsetGlyph.hh new file mode 100644 index 00000000000..8099d3c1269 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/glyf/SubsetGlyph.hh @@ -0,0 +1,152 @@ +#ifndef OT_GLYF_SUBSETGLYPH_HH +#define OT_GLYF_SUBSETGLYPH_HH + + +#include "../../hb-open-type.hh" + + +namespace OT { + +struct glyf_accelerator_t; + +namespace glyf_impl { + + +struct SubsetGlyph +{ + hb_codepoint_t old_gid; + Glyph source_glyph; + hb_bytes_t dest_start; /* region of source_glyph to copy first */ + hb_bytes_t dest_end; /* region of source_glyph to copy second */ + bool allocated; + + bool serialize (hb_serialize_context_t *c, + bool use_short_loca, + const hb_subset_plan_t *plan) const + { + TRACE_SERIALIZE (this); + + hb_bytes_t dest_glyph = dest_start.copy (c); + hb_bytes_t end_copy = dest_end.copy (c); + if (!end_copy.arrayZ || !dest_glyph.arrayZ) { + return false; + } + + dest_glyph = hb_bytes_t (&dest_glyph, dest_glyph.length + end_copy.length); + unsigned int pad_length = use_short_loca ? padding () : 0; + DEBUG_MSG (SUBSET, nullptr, "serialize %u byte glyph, width %u pad %u", dest_glyph.length, dest_glyph.length + pad_length, pad_length); + + HBUINT8 pad; + pad = 0; + while (pad_length > 0) + { + (void) c->embed (pad); + pad_length--; + } + + if (unlikely (!dest_glyph.length)) return_trace (true); + + /* update components gids. */ + for (auto &_ : Glyph (dest_glyph).get_composite_iterator ()) + { + hb_codepoint_t new_gid; + if (plan->new_gid_for_old_gid (_.get_gid(), &new_gid)) + const_cast (_).set_gid (new_gid); + } +#ifndef HB_NO_VAR_COMPOSITES + for (auto &_ : Glyph (dest_glyph).get_var_composite_iterator ()) + { + hb_codepoint_t new_gid; + if (plan->new_gid_for_old_gid (_.get_gid(), &new_gid)) + const_cast (_).set_gid (new_gid); + } +#endif + +#ifndef HB_NO_BEYOND_64K + auto it = Glyph (dest_glyph).get_composite_iterator (); + if (it) + { + /* lower GID24 to GID16 in components if possible. + * + * TODO: VarComposite. Not as critical, since VarComposite supports + * gid24 from the first version. */ + char *p = it ? (char *) &*it : nullptr; + char *q = p; + const char *end = dest_glyph.arrayZ + dest_glyph.length; + while (it) + { + auto &rec = const_cast (*it); + ++it; + + q += rec.get_size (); + + rec.lower_gid_24_to_16 (); + + unsigned size = rec.get_size (); + + memmove (p, &rec, size); + + p += size; + } + memmove (p, q, end - q); + p += end - q; + + /* We want to shorten the glyph, but we can't do that without + * updating the length in the loca table, which is already + * written out :-(. So we just fill the rest of the glyph with + * harmless instructions, since that's what they will be + * interpreted as. + * + * Should move the lowering to _populate_subset_glyphs() to + * fix this issue. */ + + hb_memset (p, 0x7A /* TrueType instruction ROFF; harmless */, end - p); + p += end - p; + dest_glyph = hb_bytes_t (dest_glyph.arrayZ, p - (char *) dest_glyph.arrayZ); + + // TODO: Padding; & trim serialized bytes. + // TODO: Update length in loca. Ugh. + } +#endif + + if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING) + Glyph (dest_glyph).drop_hints (); + + if (plan->flags & HB_SUBSET_FLAGS_SET_OVERLAPS_FLAG) + Glyph (dest_glyph).set_overlaps_flag (); + + return_trace (true); + } + + bool compile_bytes_with_deltas (const hb_subset_plan_t *plan, + hb_font_t *font, + const glyf_accelerator_t &glyf) + { + allocated = source_glyph.compile_bytes_with_deltas (plan, font, glyf, dest_start, dest_end); + return allocated; + } + + void free_compiled_bytes () + { + if (likely (allocated)) { + allocated = false; + dest_start.fini (); + dest_end.fini (); + } + } + + void drop_hints_bytes () + { source_glyph.drop_hints_bytes (dest_start, dest_end); } + + unsigned int length () const { return dest_start.length + dest_end.length; } + /* pad to 2 to ensure 2-byte loca will be ok */ + unsigned int padding () const { return length () % 2; } + unsigned int padded_size () const { return length () + padding (); } +}; + + +} /* namespace glyf_impl */ +} /* namespace OT */ + + +#endif /* OT_GLYF_SUBSETGLYPH_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/glyf/VarCompositeGlyph.hh b/src/3rdparty/harfbuzz-ng/src/OT/glyf/VarCompositeGlyph.hh new file mode 100644 index 00000000000..50cbece3cad --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/glyf/VarCompositeGlyph.hh @@ -0,0 +1,401 @@ +#ifndef OT_GLYF_VARCOMPOSITEGLYPH_HH +#define OT_GLYF_VARCOMPOSITEGLYPH_HH + + +#include "../../hb-open-type.hh" +#include "coord-setter.hh" + + +namespace OT { +namespace glyf_impl { + + +struct VarCompositeGlyphRecord +{ + protected: + enum var_composite_glyph_flag_t + { + USE_MY_METRICS = 0x0001, + AXIS_INDICES_ARE_SHORT = 0x0002, + UNIFORM_SCALE = 0x0004, + HAVE_TRANSLATE_X = 0x0008, + HAVE_TRANSLATE_Y = 0x0010, + HAVE_ROTATION = 0x0020, + HAVE_SCALE_X = 0x0040, + HAVE_SCALE_Y = 0x0080, + HAVE_SKEW_X = 0x0100, + HAVE_SKEW_Y = 0x0200, + HAVE_TCENTER_X = 0x0400, + HAVE_TCENTER_Y = 0x0800, + GID_IS_24BIT = 0x1000, + AXES_HAVE_VARIATION = 0x2000, + RESET_UNSPECIFIED_AXES = 0x4000, + }; + + public: + + unsigned int get_size () const + { + unsigned fl = flags; + unsigned int size = min_size; + + unsigned axis_width = (fl & AXIS_INDICES_ARE_SHORT) ? 4 : 3; + size += numAxes * axis_width; + + if (fl & GID_IS_24BIT) size += 1; + + // 2 bytes each for the following flags + fl = fl & (HAVE_TRANSLATE_X | HAVE_TRANSLATE_Y | + HAVE_ROTATION | + HAVE_SCALE_X | HAVE_SCALE_Y | + HAVE_SKEW_X | HAVE_SKEW_Y | + HAVE_TCENTER_X | HAVE_TCENTER_Y); + size += hb_popcount (fl) * 2; + + return size; + } + + bool has_more () const { return true; } + + bool is_use_my_metrics () const { return flags & USE_MY_METRICS; } + bool is_reset_unspecified_axes () const { return flags & RESET_UNSPECIFIED_AXES; } + + hb_codepoint_t get_gid () const + { + if (flags & GID_IS_24BIT) + return * (const HBGlyphID24 *) &pad; + else + return * (const HBGlyphID16 *) &pad; + } + + void set_gid (hb_codepoint_t gid) + { + if (flags & GID_IS_24BIT) + * (HBGlyphID24 *) &pad = gid; + else + * (HBGlyphID16 *) &pad = gid; + } + + unsigned get_numAxes () const + { + return numAxes; + } + + unsigned get_num_points () const + { + unsigned fl = flags; + unsigned num = 0; + if (fl & AXES_HAVE_VARIATION) num += numAxes; + + /* Hopefully faster code, relying on the value of the flags. */ + fl = (((fl & (HAVE_TRANSLATE_Y | HAVE_SCALE_Y | HAVE_SKEW_Y | HAVE_TCENTER_Y)) >> 1) | fl) & + (HAVE_TRANSLATE_X | HAVE_ROTATION | HAVE_SCALE_X | HAVE_SKEW_X | HAVE_TCENTER_X); + num += hb_popcount (fl); + return num; + + /* Slower but more readable code. */ + if (fl & (HAVE_TRANSLATE_X | HAVE_TRANSLATE_Y)) num++; + if (fl & HAVE_ROTATION) num++; + if (fl & (HAVE_SCALE_X | HAVE_SCALE_Y)) num++; + if (fl & (HAVE_SKEW_X | HAVE_SKEW_Y)) num++; + if (fl & (HAVE_TCENTER_X | HAVE_TCENTER_Y)) num++; + return num; + } + + void transform_points (hb_array_t record_points, + hb_array_t points) const + { + float matrix[4]; + contour_point_t trans; + + get_transformation_from_points (record_points.arrayZ, matrix, trans); + + auto arrayZ = points.arrayZ; + unsigned count = points.length; + + if (matrix[0] != 1.f || matrix[1] != 0.f || + matrix[2] != 0.f || matrix[3] != 1.f) + for (unsigned i = 0; i < count; i++) + arrayZ[i].transform (matrix); + + if (trans.x != 0.f || trans.y != 0.f) + for (unsigned i = 0; i < count; i++) + arrayZ[i].translate (trans); + } + + static inline void transform (float (&matrix)[4], contour_point_t &trans, + float (other)[6]) + { + // https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L268 + float xx1 = other[0]; + float xy1 = other[1]; + float yx1 = other[2]; + float yy1 = other[3]; + float dx1 = other[4]; + float dy1 = other[5]; + float xx2 = matrix[0]; + float xy2 = matrix[1]; + float yx2 = matrix[2]; + float yy2 = matrix[3]; + float dx2 = trans.x; + float dy2 = trans.y; + + matrix[0] = xx1*xx2 + xy1*yx2; + matrix[1] = xx1*xy2 + xy1*yy2; + matrix[2] = yx1*xx2 + yy1*yx2; + matrix[3] = yx1*xy2 + yy1*yy2; + trans.x = xx2*dx1 + yx2*dy1 + dx2; + trans.y = xy2*dx1 + yy2*dy1 + dy2; + } + + static void translate (float (&matrix)[4], contour_point_t &trans, + float translateX, float translateY) + { + if (!translateX && !translateY) + return; + + trans.x += matrix[0] * translateX + matrix[2] * translateY; + trans.y += matrix[1] * translateX + matrix[3] * translateY; + } + + static void scale (float (&matrix)[4], contour_point_t &trans, + float scaleX, float scaleY) + { + if (scaleX == 1.f && scaleY == 1.f) + return; + + matrix[0] *= scaleX; + matrix[1] *= scaleX; + matrix[2] *= scaleY; + matrix[3] *= scaleY; + } + + static void rotate (float (&matrix)[4], contour_point_t &trans, + float rotation) + { + if (!rotation) + return; + + // https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L240 + rotation = rotation * HB_PI; + float c; + float s; +#ifdef HAVE_SINCOSF + sincosf (rotation, &s, &c); +#else + c = cosf (rotation); + s = sinf (rotation); +#endif + float other[6] = {c, s, -s, c, 0.f, 0.f}; + transform (matrix, trans, other); + } + + static void skew (float (&matrix)[4], contour_point_t &trans, + float skewX, float skewY) + { + if (!skewX && !skewY) + return; + + // https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L255 + skewX = skewX * HB_PI; + skewY = skewY * HB_PI; + float other[6] = {1.f, + skewY ? tanf (skewY) : 0.f, + skewX ? tanf (skewX) : 0.f, + 1.f, + 0.f, 0.f}; + transform (matrix, trans, other); + } + + bool get_points (contour_point_vector_t &points) const + { + unsigned num_points = get_num_points (); + + points.alloc (points.length + num_points + 4); // For phantom points + if (unlikely (!points.resize (points.length + num_points, false))) return false; + contour_point_t *rec_points = points.arrayZ + (points.length - num_points); + hb_memset (rec_points, 0, num_points * sizeof (rec_points[0])); + + unsigned fl = flags; + + unsigned num_axes = numAxes; + unsigned axis_width = (fl & AXIS_INDICES_ARE_SHORT) ? 2 : 1; + unsigned axes_size = num_axes * axis_width; + + const F2DOT14 *q = (const F2DOT14 *) (axes_size + + (fl & GID_IS_24BIT ? 3 : 2) + + (const HBUINT8 *) &pad); + + unsigned count = num_axes; + if (fl & AXES_HAVE_VARIATION) + { + for (unsigned i = 0; i < count; i++) + rec_points++->x = q++->to_int (); + } + else + q += count; + + const HBUINT16 *p = (const HBUINT16 *) q; + + if (fl & (HAVE_TRANSLATE_X | HAVE_TRANSLATE_Y)) + { + int translateX = (fl & HAVE_TRANSLATE_X) ? * (const FWORD *) p++ : 0; + int translateY = (fl & HAVE_TRANSLATE_Y) ? * (const FWORD *) p++ : 0; + rec_points->x = translateX; + rec_points->y = translateY; + rec_points++; + } + if (fl & HAVE_ROTATION) + { + int rotation = (fl & HAVE_ROTATION) ? ((const F4DOT12 *) p++)->to_int () : 0; + rec_points->x = rotation; + rec_points++; + } + if (fl & (HAVE_SCALE_X | HAVE_SCALE_Y)) + { + int scaleX = (fl & HAVE_SCALE_X) ? ((const F6DOT10 *) p++)->to_int () : 1 << 10; + int scaleY = (fl & HAVE_SCALE_Y) ? ((const F6DOT10 *) p++)->to_int () : 1 << 10; + if ((fl & UNIFORM_SCALE) && !(fl & HAVE_SCALE_Y)) + scaleY = scaleX; + rec_points->x = scaleX; + rec_points->y = scaleY; + rec_points++; + } + if (fl & (HAVE_SKEW_X | HAVE_SKEW_Y)) + { + int skewX = (fl & HAVE_SKEW_X) ? ((const F4DOT12 *) p++)->to_int () : 0; + int skewY = (fl & HAVE_SKEW_Y) ? ((const F4DOT12 *) p++)->to_int () : 0; + rec_points->x = skewX; + rec_points->y = skewY; + rec_points++; + } + if (fl & (HAVE_TCENTER_X | HAVE_TCENTER_Y)) + { + int tCenterX = (fl & HAVE_TCENTER_X) ? * (const FWORD *) p++ : 0; + int tCenterY = (fl & HAVE_TCENTER_Y) ? * (const FWORD *) p++ : 0; + rec_points->x = tCenterX; + rec_points->y = tCenterY; + rec_points++; + } + + return true; + } + + void get_transformation_from_points (const contour_point_t *rec_points, + float (&matrix)[4], contour_point_t &trans) const + { + unsigned fl = flags; + + if (fl & AXES_HAVE_VARIATION) + rec_points += numAxes; + + matrix[0] = matrix[3] = 1.f; + matrix[1] = matrix[2] = 0.f; + trans.init (0.f, 0.f); + + float translateX = 0.f; + float translateY = 0.f; + float rotation = 0.f; + float scaleX = 1.f; + float scaleY = 1.f; + float skewX = 0.f; + float skewY = 0.f; + float tCenterX = 0.f; + float tCenterY = 0.f; + + if (fl & (HAVE_TRANSLATE_X | HAVE_TRANSLATE_Y)) + { + translateX = rec_points->x; + translateY = rec_points->y; + rec_points++; + } + if (fl & HAVE_ROTATION) + { + rotation = rec_points->x / (1 << 12); + rec_points++; + } + if (fl & (HAVE_SCALE_X | HAVE_SCALE_Y)) + { + scaleX = rec_points->x / (1 << 10); + scaleY = rec_points->y / (1 << 10); + rec_points++; + } + if (fl & (HAVE_SKEW_X | HAVE_SKEW_Y)) + { + skewX = rec_points->x / (1 << 12); + skewY = rec_points->y / (1 << 12); + rec_points++; + } + if (fl & (HAVE_TCENTER_X | HAVE_TCENTER_Y)) + { + tCenterX = rec_points->x; + tCenterY = rec_points->y; + rec_points++; + } + + translate (matrix, trans, translateX + tCenterX, translateY + tCenterY); + rotate (matrix, trans, rotation); + scale (matrix, trans, scaleX, scaleY); + skew (matrix, trans, -skewX, skewY); + translate (matrix, trans, -tCenterX, -tCenterY); + } + + void set_variations (coord_setter_t &setter, + hb_array_t rec_points) const + { + bool have_variations = flags & AXES_HAVE_VARIATION; + unsigned axis_width = (flags & AXIS_INDICES_ARE_SHORT) ? 2 : 1; + unsigned num_axes = numAxes; + + const HBUINT8 *p = (const HBUINT8 *) (((HBUINT8 *) &numAxes) + numAxes.static_size + (flags & GID_IS_24BIT ? 3 : 2)); + const HBUINT16 *q = (const HBUINT16 *) (((HBUINT8 *) &numAxes) + numAxes.static_size + (flags & GID_IS_24BIT ? 3 : 2)); + + const F2DOT14 *a = (const F2DOT14 *) ((HBUINT8 *) (axis_width == 1 ? (p + num_axes) : (HBUINT8 *) (q + num_axes))); + + unsigned count = num_axes; + for (unsigned i = 0; i < count; i++) + { + unsigned axis_index = axis_width == 1 ? (unsigned) *p++ : (unsigned) *q++; + + signed v = have_variations ? rec_points.arrayZ[i].x : a++->to_int (); + + v = hb_clamp (v, -(1<<14), (1<<14)); + setter[axis_index] = v; + } + } + + protected: + HBUINT16 flags; + HBUINT8 numAxes; + HBUINT16 pad; + public: + DEFINE_SIZE_MIN (5); +}; + +using var_composite_iter_t = composite_iter_tmpl; + +struct VarCompositeGlyph +{ + const GlyphHeader &header; + hb_bytes_t bytes; + VarCompositeGlyph (const GlyphHeader &header_, hb_bytes_t bytes_) : + header (header_), bytes (bytes_) {} + + var_composite_iter_t iter () const + { return var_composite_iter_t (bytes, &StructAfter (header)); } + + const hb_bytes_t trim_padding () const + { + unsigned length = GlyphHeader::static_size; + for (auto &comp : iter ()) + length += comp.get_size (); + return bytes.sub_array (0, length); + } +}; + + +} /* namespace glyf_impl */ +} /* namespace OT */ + + +#endif /* OT_GLYF_VARCOMPOSITEGLYPH_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/glyf/composite-iter.hh b/src/3rdparty/harfbuzz-ng/src/OT/glyf/composite-iter.hh new file mode 100644 index 00000000000..d05701f3d1e --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/glyf/composite-iter.hh @@ -0,0 +1,68 @@ +#ifndef OT_GLYF_COMPOSITE_ITER_HH +#define OT_GLYF_COMPOSITE_ITER_HH + + +#include "../../hb.hh" + + +namespace OT { +namespace glyf_impl { + + +template +struct composite_iter_tmpl : hb_iter_with_fallback_t, + const CompositeGlyphRecord &> +{ + typedef const CompositeGlyphRecord *__item_t__; + composite_iter_tmpl (hb_bytes_t glyph_, __item_t__ current_) : + glyph (glyph_), current (nullptr), current_size (0) + { + set_current (current_); + } + + composite_iter_tmpl () : glyph (hb_bytes_t ()), current (nullptr), current_size (0) {} + + const CompositeGlyphRecord & __item__ () const { return *current; } + bool __more__ () const { return current; } + void __next__ () + { + if (!current->has_more ()) { current = nullptr; return; } + + set_current (&StructAtOffset (current, current_size)); + } + composite_iter_tmpl __end__ () const { return composite_iter_tmpl (); } + bool operator != (const composite_iter_tmpl& o) const + { return current != o.current; } + + + void set_current (__item_t__ current_) + { + if (!glyph.check_range (current_, CompositeGlyphRecord::min_size)) + { + current = nullptr; + current_size = 0; + return; + } + unsigned size = current_->get_size (); + if (!glyph.check_range (current_, size)) + { + current = nullptr; + current_size = 0; + return; + } + + current = current_; + current_size = size; + } + + private: + hb_bytes_t glyph; + __item_t__ current; + unsigned current_size; +}; + + +} /* namespace glyf_impl */ +} /* namespace OT */ + +#endif /* OT_GLYF_COMPOSITE_ITER_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/glyf/coord-setter.hh b/src/3rdparty/harfbuzz-ng/src/OT/glyf/coord-setter.hh new file mode 100644 index 00000000000..cf05929362f --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/glyf/coord-setter.hh @@ -0,0 +1,36 @@ +#ifndef OT_GLYF_COORD_SETTER_HH +#define OT_GLYF_COORD_SETTER_HH + + +#include "../../hb.hh" + + +namespace OT { +namespace glyf_impl { + + +struct coord_setter_t +{ + coord_setter_t (hb_array_t coords) : + coords (coords) {} + + int& operator [] (unsigned idx) + { + if (unlikely (idx >= HB_GLYF_VAR_COMPOSITE_MAX_AXES)) + return Crap(int); + if (coords.length < idx + 1) + coords.resize (idx + 1); + return coords[idx]; + } + + hb_array_t get_coords () + { return coords.as_array (); } + + hb_vector_t coords; +}; + + +} /* namespace glyf_impl */ +} /* namespace OT */ + +#endif /* OT_GLYF_COORD_SETTER_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/glyf/glyf-helpers.hh b/src/3rdparty/harfbuzz-ng/src/OT/glyf/glyf-helpers.hh new file mode 100644 index 00000000000..d0a5a132f05 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/glyf/glyf-helpers.hh @@ -0,0 +1,127 @@ +#ifndef OT_GLYF_GLYF_HELPERS_HH +#define OT_GLYF_GLYF_HELPERS_HH + + +#include "../../hb-open-type.hh" +#include "../../hb-subset-plan.hh" + +#include "loca.hh" + + +namespace OT { +namespace glyf_impl { + + +template +static void +_write_loca (IteratorIn&& it, + const hb_sorted_vector_t new_to_old_gid_list, + bool short_offsets, + TypeOut *dest, + unsigned num_offsets) +{ + unsigned right_shift = short_offsets ? 1 : 0; + unsigned offset = 0; + TypeOut value; + value = 0; + *dest++ = value; + hb_codepoint_t last = 0; + for (auto _ : new_to_old_gid_list) + { + hb_codepoint_t gid = _.first; + for (; last < gid; last++) + { + DEBUG_MSG (SUBSET, nullptr, "loca entry empty offset %u", offset); + *dest++ = value; + } + + unsigned padded_size = *it++; + offset += padded_size; + DEBUG_MSG (SUBSET, nullptr, "loca entry gid %u offset %u padded-size %u", gid, offset, padded_size); + value = offset >> right_shift; + *dest++ = value; + + last++; // Skip over gid + } + unsigned num_glyphs = num_offsets - 1; + for (; last < num_glyphs; last++) + { + DEBUG_MSG (SUBSET, nullptr, "loca entry empty offset %u", offset); + *dest++ = value; + } +} + +static bool +_add_head_and_set_loca_version (hb_subset_plan_t *plan, bool use_short_loca) +{ + hb_blob_t *head_blob = hb_sanitize_context_t ().reference_table (plan->source); + hb_blob_t *head_prime_blob = hb_blob_copy_writable_or_fail (head_blob); + hb_blob_destroy (head_blob); + + if (unlikely (!head_prime_blob)) + return false; + + head *head_prime = (head *) hb_blob_get_data_writable (head_prime_blob, nullptr); + head_prime->indexToLocFormat = use_short_loca ? 0 : 1; + if (plan->normalized_coords) + { + head_prime->xMin = plan->head_maxp_info.xMin; + head_prime->xMax = plan->head_maxp_info.xMax; + head_prime->yMin = plan->head_maxp_info.yMin; + head_prime->yMax = plan->head_maxp_info.yMax; + + unsigned orig_flag = head_prime->flags; + if (plan->head_maxp_info.allXMinIsLsb) + orig_flag |= 1 << 1; + else + orig_flag &= ~(1 << 1); + head_prime->flags = orig_flag; + } + bool success = plan->add_table (HB_OT_TAG_head, head_prime_blob); + + hb_blob_destroy (head_prime_blob); + return success; +} + +template +static bool +_add_loca_and_head (hb_subset_context_t *c, + Iterator padded_offsets, + bool use_short_loca) +{ + unsigned num_offsets = c->plan->num_output_glyphs () + 1; + unsigned entry_size = use_short_loca ? 2 : 4; + + char *loca_prime_data = (char *) hb_malloc (entry_size * num_offsets); + + if (unlikely (!loca_prime_data)) return false; + + DEBUG_MSG (SUBSET, nullptr, "loca entry_size %u num_offsets %u size %u", + entry_size, num_offsets, entry_size * num_offsets); + + if (use_short_loca) + _write_loca (padded_offsets, c->plan->new_to_old_gid_list, true, (HBUINT16 *) loca_prime_data, num_offsets); + else + _write_loca (padded_offsets, c->plan->new_to_old_gid_list, false, (HBUINT32 *) loca_prime_data, num_offsets); + + hb_blob_t *loca_blob = hb_blob_create (loca_prime_data, + entry_size * num_offsets, + HB_MEMORY_MODE_WRITABLE, + loca_prime_data, + hb_free); + + bool result = c->plan->add_table (HB_OT_TAG_loca, loca_blob) + && _add_head_and_set_loca_version (c->plan, use_short_loca); + + hb_blob_destroy (loca_blob); + return result; +} + + +} /* namespace glyf_impl */ +} /* namespace OT */ + + +#endif /* OT_GLYF_GLYF_HELPERS_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/glyf/glyf.hh b/src/3rdparty/harfbuzz-ng/src/OT/glyf/glyf.hh new file mode 100644 index 00000000000..6300cf4be0c --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/glyf/glyf.hh @@ -0,0 +1,504 @@ +#ifndef OT_GLYF_GLYF_HH +#define OT_GLYF_GLYF_HH + + +#include "../../hb-open-type.hh" +#include "../../hb-ot-head-table.hh" +#include "../../hb-ot-hmtx-table.hh" +#include "../../hb-ot-var-gvar-table.hh" +#include "../../hb-draw.hh" +#include "../../hb-paint.hh" + +#include "glyf-helpers.hh" +#include "Glyph.hh" +#include "SubsetGlyph.hh" +#include "loca.hh" +#include "path-builder.hh" + + +namespace OT { + + +/* + * glyf -- TrueType Glyph Data + * https://docs.microsoft.com/en-us/typography/opentype/spec/glyf + */ +#define HB_OT_TAG_glyf HB_TAG('g','l','y','f') + +struct glyf +{ + friend struct glyf_accelerator_t; + + static constexpr hb_tag_t tableTag = HB_OT_TAG_glyf; + + static bool has_valid_glyf_format(const hb_face_t* face) + { + const OT::head &head = *face->table.head; + return head.indexToLocFormat <= 1 && head.glyphDataFormat <= 1; + } + + bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const + { + TRACE_SANITIZE (this); + /* Runtime checks as eager sanitizing each glyph is costy */ + return_trace (true); + } + + /* requires source of SubsetGlyph complains the identifier isn't declared */ + template + bool serialize (hb_serialize_context_t *c, + Iterator it, + bool use_short_loca, + const hb_subset_plan_t *plan) + { + TRACE_SERIALIZE (this); + + unsigned init_len = c->length (); + for (auto &_ : it) + if (unlikely (!_.serialize (c, use_short_loca, plan))) + return false; + + /* As a special case when all glyph in the font are empty, add a zero byte + * to the table, so that OTS doesn’t reject it, and to make the table work + * on Windows as well. + * See https://github.com/khaledhosny/ots/issues/52 */ + if (init_len == c->length ()) + { + HBUINT8 empty_byte; + empty_byte = 0; + c->copy (empty_byte); + } + return_trace (true); + } + + /* Byte region(s) per glyph to output + unpadded, hints removed if so requested + If we fail to process a glyph we produce an empty (0-length) glyph */ + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + if (!has_valid_glyf_format (c->plan->source)) { + // glyf format is unknown don't attempt to subset it. + DEBUG_MSG (SUBSET, nullptr, + "unkown glyf format, dropping from subset."); + return_trace (false); + } + + hb_font_t *font = nullptr; + if (c->plan->normalized_coords) + { + font = _create_font_for_instancing (c->plan); + if (unlikely (!font)) + return_trace (false); + } + + hb_vector_t padded_offsets; + if (unlikely (!padded_offsets.alloc (c->plan->new_to_old_gid_list.length, true))) + return_trace (false); + + hb_vector_t glyphs; + if (!_populate_subset_glyphs (c->plan, font, glyphs)) + { + hb_font_destroy (font); + return_trace (false); + } + + if (font) + hb_font_destroy (font); + + unsigned max_offset = 0; + for (auto &g : glyphs) + { + unsigned size = g.padded_size (); + padded_offsets.push (size); + max_offset += size; + } + + bool use_short_loca = false; + if (likely (!c->plan->force_long_loca)) + use_short_loca = max_offset < 0x1FFFF; + + if (!use_short_loca) + { + padded_offsets.resize (0); + for (auto &g : glyphs) + padded_offsets.push (g.length ()); + } + + auto *glyf_prime = c->serializer->start_embed (); + bool result = glyf_prime->serialize (c->serializer, hb_iter (glyphs), use_short_loca, c->plan); + if (c->plan->normalized_coords && !c->plan->pinned_at_default) + _free_compiled_subset_glyphs (glyphs); + + if (unlikely (!c->serializer->check_success (glyf_impl::_add_loca_and_head (c, + padded_offsets.iter (), + use_short_loca)))) + return_trace (false); + + return result; + } + + bool + _populate_subset_glyphs (const hb_subset_plan_t *plan, + hb_font_t *font, + hb_vector_t& glyphs /* OUT */) const; + + hb_font_t * + _create_font_for_instancing (const hb_subset_plan_t *plan) const; + + void _free_compiled_subset_glyphs (hb_vector_t &glyphs) const + { + for (auto &g : glyphs) + g.free_compiled_bytes (); + } + + protected: + UnsizedArrayOf + dataZ; /* Glyphs data. */ + public: + DEFINE_SIZE_MIN (0); /* In reality, this is UNBOUNDED() type; but since we always + * check the size externally, allow Null() object of it by + * defining it _MIN instead. */ +}; + +struct glyf_accelerator_t +{ + glyf_accelerator_t (hb_face_t *face) + { + short_offset = false; + num_glyphs = 0; + loca_table = nullptr; + glyf_table = nullptr; +#ifndef HB_NO_VAR + gvar = nullptr; +#endif + hmtx = nullptr; +#ifndef HB_NO_VERTICAL + vmtx = nullptr; +#endif + const OT::head &head = *face->table.head; + if (!glyf::has_valid_glyf_format (face)) + /* Unknown format. Leave num_glyphs=0, that takes care of disabling us. */ + return; + short_offset = 0 == head.indexToLocFormat; + + loca_table = face->table.loca.get_blob (); // Needs no destruct! + glyf_table = hb_sanitize_context_t ().reference_table (face); +#ifndef HB_NO_VAR + gvar = face->table.gvar; +#endif + hmtx = face->table.hmtx; +#ifndef HB_NO_VERTICAL + vmtx = face->table.vmtx; +#endif + + num_glyphs = hb_max (1u, loca_table.get_length () / (short_offset ? 2 : 4)) - 1; + num_glyphs = hb_min (num_glyphs, face->get_num_glyphs ()); + } + ~glyf_accelerator_t () + { + glyf_table.destroy (); + } + + bool has_data () const { return num_glyphs; } + + protected: + template + bool get_points (hb_font_t *font, hb_codepoint_t gid, T consumer) const + { + if (gid >= num_glyphs) return false; + + /* Making this allocfree is not that easy + https://github.com/harfbuzz/harfbuzz/issues/2095 + mostly because of gvar handling in VF fonts, + perhaps a separate path for non-VF fonts can be considered */ + contour_point_vector_t all_points; + + bool phantom_only = !consumer.is_consuming_contour_points (); + if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, nullptr, nullptr, nullptr, true, true, phantom_only))) + return false; + + unsigned count = all_points.length; + assert (count >= glyf_impl::PHANTOM_COUNT); + count -= glyf_impl::PHANTOM_COUNT; + + if (consumer.is_consuming_contour_points ()) + { + for (auto &point : all_points.as_array ().sub_array (0, count)) + consumer.consume_point (point); + consumer.points_end (); + } + + /* Where to write phantoms, nullptr if not requested */ + contour_point_t *phantoms = consumer.get_phantoms_sink (); + if (phantoms) + for (unsigned i = 0; i < glyf_impl::PHANTOM_COUNT; ++i) + phantoms[i] = all_points.arrayZ[count + i]; + + return true; + } + + public: + +#ifndef HB_NO_VAR + struct points_aggregator_t + { + hb_font_t *font; + hb_glyph_extents_t *extents; + contour_point_t *phantoms; + bool scaled; + + struct contour_bounds_t + { + contour_bounds_t () { min_x = min_y = FLT_MAX; max_x = max_y = -FLT_MAX; } + + void add (const contour_point_t &p) + { + min_x = hb_min (min_x, p.x); + min_y = hb_min (min_y, p.y); + max_x = hb_max (max_x, p.x); + max_y = hb_max (max_y, p.y); + } + + bool empty () const { return (min_x >= max_x) || (min_y >= max_y); } + + void get_extents (hb_font_t *font, hb_glyph_extents_t *extents, bool scaled) + { + if (unlikely (empty ())) + { + extents->width = 0; + extents->x_bearing = 0; + extents->height = 0; + extents->y_bearing = 0; + return; + } + { + extents->x_bearing = roundf (min_x); + extents->width = roundf (max_x - extents->x_bearing); + extents->y_bearing = roundf (max_y); + extents->height = roundf (min_y - extents->y_bearing); + + if (scaled) + font->scale_glyph_extents (extents); + } + } + + protected: + float min_x, min_y, max_x, max_y; + } bounds; + + points_aggregator_t (hb_font_t *font_, hb_glyph_extents_t *extents_, contour_point_t *phantoms_, bool scaled_) + { + font = font_; + extents = extents_; + phantoms = phantoms_; + scaled = scaled_; + if (extents) bounds = contour_bounds_t (); + } + + HB_ALWAYS_INLINE + void consume_point (const contour_point_t &point) { bounds.add (point); } + void points_end () { bounds.get_extents (font, extents, scaled); } + + bool is_consuming_contour_points () { return extents; } + contour_point_t *get_phantoms_sink () { return phantoms; } + }; + + unsigned + get_advance_with_var_unscaled (hb_font_t *font, hb_codepoint_t gid, bool is_vertical) const + { + if (unlikely (gid >= num_glyphs)) return 0; + + bool success = false; + + contour_point_t phantoms[glyf_impl::PHANTOM_COUNT]; + if (font->num_coords) + success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms, false)); + + if (unlikely (!success)) + return +#ifndef HB_NO_VERTICAL + is_vertical ? vmtx->get_advance_without_var_unscaled (gid) : +#endif + hmtx->get_advance_without_var_unscaled (gid); + + float result = is_vertical + ? phantoms[glyf_impl::PHANTOM_TOP].y - phantoms[glyf_impl::PHANTOM_BOTTOM].y + : phantoms[glyf_impl::PHANTOM_RIGHT].x - phantoms[glyf_impl::PHANTOM_LEFT].x; + return hb_clamp (roundf (result), 0.f, (float) UINT_MAX / 2); + } + + bool get_leading_bearing_with_var_unscaled (hb_font_t *font, hb_codepoint_t gid, bool is_vertical, int *lsb) const + { + if (unlikely (gid >= num_glyphs)) return false; + + hb_glyph_extents_t extents; + + contour_point_t phantoms[glyf_impl::PHANTOM_COUNT]; + if (unlikely (!get_points (font, gid, points_aggregator_t (font, &extents, phantoms, false)))) + return false; + + *lsb = is_vertical + ? roundf (phantoms[glyf_impl::PHANTOM_TOP].y) - extents.y_bearing + : roundf (phantoms[glyf_impl::PHANTOM_LEFT].x); + return true; + } +#endif + + bool get_leading_bearing_without_var_unscaled (hb_codepoint_t gid, bool is_vertical, int *lsb) const + { + if (unlikely (gid >= num_glyphs)) return false; + if (is_vertical) return false; // TODO Humm, what to do here? + + *lsb = glyph_for_gid (gid).get_header ()->xMin; + return true; + } + + public: + bool get_extents (hb_font_t *font, hb_codepoint_t gid, hb_glyph_extents_t *extents) const + { + if (unlikely (gid >= num_glyphs)) return false; + +#ifndef HB_NO_VAR + if (font->num_coords) + return get_points (font, gid, points_aggregator_t (font, extents, nullptr, true)); +#endif + return glyph_for_gid (gid).get_extents_without_var_scaled (font, *this, extents); + } + + bool paint_glyph (hb_font_t *font, hb_codepoint_t gid, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const + { + funcs->push_clip_glyph (data, gid, font); + funcs->color (data, true, foreground); + funcs->pop_clip (data); + + return true; + } + + const glyf_impl::Glyph + glyph_for_gid (hb_codepoint_t gid, bool needs_padding_removal = false) const + { + if (unlikely (gid >= num_glyphs)) return glyf_impl::Glyph (); + + unsigned int start_offset, end_offset; + + if (short_offset) + { + const HBUINT16 *offsets = (const HBUINT16 *) loca_table->dataZ.arrayZ; + start_offset = 2 * offsets[gid]; + end_offset = 2 * offsets[gid + 1]; + } + else + { + const HBUINT32 *offsets = (const HBUINT32 *) loca_table->dataZ.arrayZ; + start_offset = offsets[gid]; + end_offset = offsets[gid + 1]; + } + + if (unlikely (start_offset > end_offset || end_offset > glyf_table.get_length ())) + return glyf_impl::Glyph (); + + glyf_impl::Glyph glyph (hb_bytes_t ((const char *) this->glyf_table + start_offset, + end_offset - start_offset), gid); + return needs_padding_removal ? glyf_impl::Glyph (glyph.trim_padding (), gid) : glyph; + } + + bool + get_path (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session) const + { return get_points (font, gid, glyf_impl::path_builder_t (font, draw_session)); } + +#ifndef HB_NO_VAR + const gvar_accelerator_t *gvar; +#endif + const hmtx_accelerator_t *hmtx; +#ifndef HB_NO_VERTICAL + const vmtx_accelerator_t *vmtx; +#endif + + private: + bool short_offset; + unsigned int num_glyphs; + hb_blob_ptr_t loca_table; + hb_blob_ptr_t glyf_table; +}; + + +inline bool +glyf::_populate_subset_glyphs (const hb_subset_plan_t *plan, + hb_font_t *font, + hb_vector_t& glyphs /* OUT */) const +{ + OT::glyf_accelerator_t glyf (plan->source); + if (!glyphs.alloc (plan->new_to_old_gid_list.length, true)) return false; + + for (const auto &pair : plan->new_to_old_gid_list) + { + hb_codepoint_t new_gid = pair.first; + hb_codepoint_t old_gid = pair.second; + glyf_impl::SubsetGlyph *p = glyphs.push (); + glyf_impl::SubsetGlyph& subset_glyph = *p; + subset_glyph.old_gid = old_gid; + + if (unlikely (old_gid == 0 && new_gid == 0 && + !(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE)) && + !plan->normalized_coords) + subset_glyph.source_glyph = glyf_impl::Glyph (); + else + { + /* If plan has an accelerator, the preprocessing step already trimmed glyphs. + * Don't trim them again! */ + subset_glyph.source_glyph = glyf.glyph_for_gid (subset_glyph.old_gid, !plan->accelerator); + } + + if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING) + subset_glyph.drop_hints_bytes (); + else + subset_glyph.dest_start = subset_glyph.source_glyph.get_bytes (); + + if (font) + { + if (unlikely (!subset_glyph.compile_bytes_with_deltas (plan, font, glyf))) + { + // when pinned at default, only bounds are updated, thus no need to free + if (!plan->pinned_at_default) + _free_compiled_subset_glyphs (glyphs); + return false; + } + } + } + return true; +} + +inline hb_font_t * +glyf::_create_font_for_instancing (const hb_subset_plan_t *plan) const +{ + hb_font_t *font = hb_font_create (plan->source); + if (unlikely (font == hb_font_get_empty ())) return nullptr; + + hb_vector_t vars; + if (unlikely (!vars.alloc (plan->user_axes_location.get_population (), true))) + { + hb_font_destroy (font); + return nullptr; + } + + for (auto _ : plan->user_axes_location) + { + hb_variation_t var; + var.tag = _.first; + var.value = _.second.middle; + vars.push (var); + } + +#ifndef HB_NO_VAR + hb_font_set_variations (font, vars.arrayZ, plan->user_axes_location.get_population ()); +#endif + return font; +} + + +} /* namespace OT */ + + +#endif /* OT_GLYF_GLYF_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/glyf/loca.hh b/src/3rdparty/harfbuzz-ng/src/OT/glyf/loca.hh new file mode 100644 index 00000000000..4481cba8ed2 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/glyf/loca.hh @@ -0,0 +1,43 @@ +#ifndef OT_GLYF_LOCA_HH +#define OT_GLYF_LOCA_HH + + +#include "../../hb-open-type.hh" + + +namespace OT { + + +/* + * loca -- Index to Location + * https://docs.microsoft.com/en-us/typography/opentype/spec/loca + */ +#define HB_OT_TAG_loca HB_TAG('l','o','c','a') + +struct loca +{ + friend struct glyf; + friend struct glyf_accelerator_t; + + static constexpr hb_tag_t tableTag = HB_OT_TAG_loca; + + bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const + { + TRACE_SANITIZE (this); + return_trace (true); + } + + protected: + UnsizedArrayOf + dataZ; /* Location data. */ + public: + DEFINE_SIZE_MIN (0); /* In reality, this is UNBOUNDED() type; but since we always + * check the size externally, allow Null() object of it by + * defining it _MIN instead. */ +}; + + +} /* namespace OT */ + + +#endif /* OT_GLYF_LOCA_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/glyf/path-builder.hh b/src/3rdparty/harfbuzz-ng/src/OT/glyf/path-builder.hh new file mode 100644 index 00000000000..f5505245033 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/glyf/path-builder.hh @@ -0,0 +1,190 @@ +#ifndef OT_GLYF_PATH_BUILDER_HH +#define OT_GLYF_PATH_BUILDER_HH + + +#include "../../hb.hh" + + +namespace OT { +namespace glyf_impl { + + +struct path_builder_t +{ + hb_font_t *font; + hb_draw_session_t *draw_session; + + struct optional_point_t + { + optional_point_t () {} + optional_point_t (float x_, float y_) : has_data (true), x (x_), y (y_) {} + operator bool () const { return has_data; } + + bool has_data = false; + float x; + float y; + + optional_point_t mid (optional_point_t p) + { return optional_point_t ((x + p.x) * 0.5f, (y + p.y) * 0.5f); } + } first_oncurve, first_offcurve, first_offcurve2, last_offcurve, last_offcurve2; + + path_builder_t (hb_font_t *font_, hb_draw_session_t &draw_session_) : + font (font_), draw_session (&draw_session_) {} + + /* based on https://github.com/RazrFalcon/ttf-parser/blob/4f32821/src/glyf.rs#L287 + See also: + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM01/Chap1.html + * https://stackoverflow.com/a/20772557 + * + * Cubic support added. */ + HB_ALWAYS_INLINE + void consume_point (const contour_point_t &point) + { + bool is_on_curve = point.flag & glyf_impl::SimpleGlyph::FLAG_ON_CURVE; +#ifdef HB_NO_CUBIC_GLYF + bool is_cubic = false; +#else + bool is_cubic = !is_on_curve && (point.flag & glyf_impl::SimpleGlyph::FLAG_CUBIC); +#endif + optional_point_t p (font->em_fscalef_x (point.x), font->em_fscalef_y (point.y)); + if (unlikely (!first_oncurve)) + { + if (is_on_curve) + { + first_oncurve = p; + draw_session->move_to (p.x, p.y); + } + else + { + if (is_cubic && !first_offcurve2) + { + first_offcurve2 = first_offcurve; + first_offcurve = p; + } + else if (first_offcurve) + { + optional_point_t mid = first_offcurve.mid (p); + first_oncurve = mid; + last_offcurve = p; + draw_session->move_to (mid.x, mid.y); + } + else + first_offcurve = p; + } + } + else + { + if (last_offcurve) + { + if (is_on_curve) + { + if (last_offcurve2) + { + draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y, + last_offcurve.x, last_offcurve.y, + p.x, p.y); + last_offcurve2 = optional_point_t (); + } + else + draw_session->quadratic_to (last_offcurve.x, last_offcurve.y, + p.x, p.y); + last_offcurve = optional_point_t (); + } + else + { + if (is_cubic && !last_offcurve2) + { + last_offcurve2 = last_offcurve; + last_offcurve = p; + } + else + { + optional_point_t mid = last_offcurve.mid (p); + + if (is_cubic) + { + draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y, + last_offcurve.x, last_offcurve.y, + mid.x, mid.y); + last_offcurve2 = optional_point_t (); + } + else + draw_session->quadratic_to (last_offcurve.x, last_offcurve.y, + mid.x, mid.y); + last_offcurve = p; + } + } + } + else + { + if (is_on_curve) + draw_session->line_to (p.x, p.y); + else + last_offcurve = p; + } + } + + if (unlikely (point.is_end_point)) + { + if (first_offcurve && last_offcurve) + { + optional_point_t mid = last_offcurve.mid (first_offcurve2 ? + first_offcurve2 : + first_offcurve); + if (last_offcurve2) + draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y, + last_offcurve.x, last_offcurve.y, + mid.x, mid.y); + else + draw_session->quadratic_to (last_offcurve.x, last_offcurve.y, + mid.x, mid.y); + last_offcurve = optional_point_t (); + } + /* now check the rest */ + + if (first_offcurve && first_oncurve) + { + if (first_offcurve2) + draw_session->cubic_to (first_offcurve2.x, first_offcurve2.y, + first_offcurve.x, first_offcurve.y, + first_oncurve.x, first_oncurve.y); + else + draw_session->quadratic_to (first_offcurve.x, first_offcurve.y, + first_oncurve.x, first_oncurve.y); + } + else if (last_offcurve && first_oncurve) + { + if (last_offcurve2) + draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y, + last_offcurve.x, last_offcurve.y, + first_oncurve.x, first_oncurve.y); + else + draw_session->quadratic_to (last_offcurve.x, last_offcurve.y, + first_oncurve.x, first_oncurve.y); + } + else if (first_oncurve) + draw_session->line_to (first_oncurve.x, first_oncurve.y); + else if (first_offcurve) + { + float x = first_offcurve.x, y = first_offcurve.y; + draw_session->move_to (x, y); + draw_session->quadratic_to (x, y, x, y); + } + + /* Getting ready for the next contour */ + first_oncurve = first_offcurve = last_offcurve = last_offcurve2 = optional_point_t (); + draw_session->close_path (); + } + } + void points_end () {} + + bool is_consuming_contour_points () { return true; } + contour_point_t *get_phantoms_sink () { return nullptr; } +}; + + +} /* namespace glyf_impl */ +} /* namespace OT */ + + +#endif /* OT_GLYF_PATH_BUILDER_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/OT/name/name.hh b/src/3rdparty/harfbuzz-ng/src/OT/name/name.hh new file mode 100644 index 00000000000..c8de1013456 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/OT/name/name.hh @@ -0,0 +1,586 @@ +/* + * Copyright © 2011,2012 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): Behdad Esfahbod + */ + +#ifndef OT_NAME_NAME_HH +#define OT_NAME_NAME_HH + +#include "../../hb-open-type.hh" +#include "../../hb-ot-name-language.hh" +#include "../../hb-aat-layout.hh" +#include "../../hb-utf.hh" + + +namespace OT { + +template +inline unsigned int +hb_ot_name_convert_utf (hb_bytes_t bytes, + unsigned int *text_size /* IN/OUT */, + typename out_utf_t::codepoint_t *text /* OUT */) +{ + unsigned int src_len = bytes.length / sizeof (typename in_utf_t::codepoint_t); + const typename in_utf_t::codepoint_t *src = (const typename in_utf_t::codepoint_t *) bytes.arrayZ; + const typename in_utf_t::codepoint_t *src_end = src + src_len; + + typename out_utf_t::codepoint_t *dst = text; + + hb_codepoint_t unicode; + const hb_codepoint_t replacement = HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT; + + if (text_size && *text_size) + { + (*text_size)--; /* Save room for NUL-termination. */ + const typename out_utf_t::codepoint_t *dst_end = text + *text_size; + + while (src < src_end && dst < dst_end) + { + const typename in_utf_t::codepoint_t *src_next = in_utf_t::next (src, src_end, &unicode, replacement); + typename out_utf_t::codepoint_t *dst_next = out_utf_t::encode (dst, dst_end, unicode); + if (dst_next == dst) + break; /* Out-of-room. */ + + dst = dst_next; + src = src_next; + } + + *text_size = dst - text; + *dst = 0; /* NUL-terminate. */ + } + + /* Accumulate length of rest. */ + unsigned int dst_len = dst - text; + while (src < src_end) + { + src = in_utf_t::next (src, src_end, &unicode, replacement); + dst_len += out_utf_t::encode_len (unicode); + } + return dst_len; +} + +#define entry_score var.u16[0] +#define entry_index var.u16[1] + + +/* + * name -- Naming + * https://docs.microsoft.com/en-us/typography/opentype/spec/name + */ +#define HB_OT_TAG_name HB_TAG('n','a','m','e') + +#define UNSUPPORTED 42 + +struct NameRecord +{ + hb_language_t language (hb_face_t *face) const + { +#ifndef HB_NO_OT_NAME_LANGUAGE + unsigned int p = platformID; + unsigned int l = languageID; + + if (p == 3) + return _hb_ot_name_language_for_ms_code (l); + + if (p == 1) + return _hb_ot_name_language_for_mac_code (l); + +#ifndef HB_NO_OT_NAME_LANGUAGE_AAT + if (p == 0) + return face->table.ltag->get_language (l); +#endif + +#endif + return HB_LANGUAGE_INVALID; + } + + uint16_t score () const + { + /* Same order as in cmap::find_best_subtable(). */ + unsigned int p = platformID; + unsigned int e = encodingID; + + /* 32-bit. */ + if (p == 3 && e == 10) return 0; + if (p == 0 && e == 6) return 1; + if (p == 0 && e == 4) return 2; + + /* 16-bit. */ + if (p == 3 && e == 1) return 3; + if (p == 0 && e == 3) return 4; + if (p == 0 && e == 2) return 5; + if (p == 0 && e == 1) return 6; + if (p == 0 && e == 0) return 7; + + /* Symbol. */ + if (p == 3 && e == 0) return 8; + + /* We treat all Mac Latin names as ASCII only. */ + if (p == 1 && e == 0) return 10; /* 10 is magic number :| */ + + return UNSUPPORTED; + } + + NameRecord* copy (hb_serialize_context_t *c, const void *base +#ifdef HB_EXPERIMENTAL_API + , const hb_hashmap_t *name_table_overrides +#endif + ) const + { + TRACE_SERIALIZE (this); + HB_UNUSED auto snap = c->snapshot (); + auto *out = c->embed (this); + if (unlikely (!out)) return_trace (nullptr); +#ifdef HB_EXPERIMENTAL_API + hb_ot_name_record_ids_t record_ids (platformID, encodingID, languageID, nameID); + hb_bytes_t* name_bytes; + + if (name_table_overrides->has (record_ids, &name_bytes)) { + hb_bytes_t encoded_bytes = *name_bytes; + char *name_str_utf16_be = nullptr; + + if (platformID != 1) + { + unsigned text_size = hb_ot_name_convert_utf (*name_bytes, nullptr, nullptr); + + text_size++; // needs to consider NULL terminator for use in hb_ot_name_convert_utf() + unsigned byte_len = text_size * hb_utf16_be_t::codepoint_t::static_size; + name_str_utf16_be = (char *) hb_calloc (byte_len, 1); + if (!name_str_utf16_be) + { + c->revert (snap); + return_trace (nullptr); + } + hb_ot_name_convert_utf (*name_bytes, &text_size, + (hb_utf16_be_t::codepoint_t *) name_str_utf16_be); + + unsigned encoded_byte_len = text_size * hb_utf16_be_t::codepoint_t::static_size; + if (!encoded_byte_len || !c->check_assign (out->length, encoded_byte_len, HB_SERIALIZE_ERROR_INT_OVERFLOW)) { + c->revert (snap); + hb_free (name_str_utf16_be); + return_trace (nullptr); + } + + encoded_bytes = hb_bytes_t (name_str_utf16_be, encoded_byte_len); + } + else + { + // mac platform, copy the UTF-8 string(all ascii characters) as is + if (!c->check_assign (out->length, encoded_bytes.length, HB_SERIALIZE_ERROR_INT_OVERFLOW)) { + c->revert (snap); + return_trace (nullptr); + } + } + + out->offset = 0; + c->push (); + encoded_bytes.copy (c); + c->add_link (out->offset, c->pop_pack (), hb_serialize_context_t::Tail, 0); + hb_free (name_str_utf16_be); + } + else +#endif + { + out->offset.serialize_copy (c, offset, base, 0, hb_serialize_context_t::Tail, length); + } + return_trace (out); + } + + bool isUnicode () const + { + unsigned int p = platformID; + unsigned int e = encodingID; + + return (p == 0 || + (p == 3 && (e == 0 || e == 1 || e == 10))); + } + + static int cmp (const void *pa, const void *pb) + { + const NameRecord *a = (const NameRecord *)pa; + const NameRecord *b = (const NameRecord *)pb; + + if (a->platformID != b->platformID) + return a->platformID - b->platformID; + + if (a->encodingID != b->encodingID) + return a->encodingID - b->encodingID; + + if (a->languageID != b->languageID) + return a->languageID - b->languageID; + + if (a->nameID != b->nameID) + return a->nameID - b->nameID; + + if (a->length != b->length) + return a->length - b->length; + + return 0; + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && offset.sanitize (c, base, length)); + } + + HBUINT16 platformID; /* Platform ID. */ + HBUINT16 encodingID; /* Platform-specific encoding ID. */ + HBUINT16 languageID; /* Language ID. */ + HBUINT16 nameID; /* Name ID. */ + HBUINT16 length; /* String length (in bytes). */ + NNOffset16To> + offset; /* String offset from start of storage area (in bytes). */ + public: + DEFINE_SIZE_STATIC (12); +}; + +static int +_hb_ot_name_entry_cmp_key (const void *pa, const void *pb, bool exact) +{ + const hb_ot_name_entry_t *a = (const hb_ot_name_entry_t *) pa; + const hb_ot_name_entry_t *b = (const hb_ot_name_entry_t *) pb; + + /* Compare by name_id, then language. */ + + if (a->name_id != b->name_id) + return a->name_id - b->name_id; + + if (a->language == b->language) return 0; + if (!a->language) return -1; + if (!b->language) return +1; + + const char *astr = hb_language_to_string (a->language); + const char *bstr = hb_language_to_string (b->language); + + signed c = strcmp (astr, bstr); + + // 'a' is the user request, and 'b' is string in the font. + // If eg. user asks for "en-us" and font has "en", approve. + if (!exact && c && + hb_language_matches (b->language, a->language)) + return 0; + + return c; +} + +static int +_hb_ot_name_entry_cmp (const void *pa, const void *pb) +{ + /* Compare by name_id, then language, then score, then index. */ + + int v = _hb_ot_name_entry_cmp_key (pa, pb, true); + if (v) + return v; + + const hb_ot_name_entry_t *a = (const hb_ot_name_entry_t *) pa; + const hb_ot_name_entry_t *b = (const hb_ot_name_entry_t *) pb; + + if (a->entry_score != b->entry_score) + return a->entry_score - b->entry_score; + + if (a->entry_index != b->entry_index) + return a->entry_index - b->entry_index; + + return 0; +} + +struct name +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_name; + + unsigned int get_size () const + { return min_size + count * nameRecordZ.item_size; } + + template + bool serialize (hb_serialize_context_t *c, + Iterator it, + const void *src_string_pool +#ifdef HB_EXPERIMENTAL_API + , const hb_vector_t& insert_name_records + , const hb_hashmap_t *name_table_overrides +#endif + ) + { + TRACE_SERIALIZE (this); + + if (unlikely (!c->extend_min ((*this)))) return_trace (false); + + unsigned total_count = it.len () +#ifdef HB_EXPERIMENTAL_API + + insert_name_records.length +#endif + ; + this->format = 0; + if (!c->check_assign (this->count, total_count, HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return false; + + NameRecord *name_records = (NameRecord *) hb_calloc (total_count, NameRecord::static_size); + if (unlikely (!name_records)) return_trace (false); + + hb_array_t records (name_records, total_count); + + for (const NameRecord& record : it) + { + hb_memcpy (name_records, &record, NameRecord::static_size); + name_records++; + } + +#ifdef HB_EXPERIMENTAL_API + for (unsigned i = 0; i < insert_name_records.length; i++) + { + const hb_ot_name_record_ids_t& ids = insert_name_records[i]; + NameRecord record; + record.platformID = ids.platform_id; + record.encodingID = ids.encoding_id; + record.languageID = ids.language_id; + record.nameID = ids.name_id; + record.length = 0; // handled in NameRecord copy() + record.offset = 0; + hb_memcpy (name_records, &record, NameRecord::static_size); + name_records++; + } +#endif + + records.qsort (); + + c->copy_all (records, + src_string_pool +#ifdef HB_EXPERIMENTAL_API + , name_table_overrides +#endif + ); + hb_free (records.arrayZ); + + + if (unlikely (c->ran_out_of_room ())) return_trace (false); + + this->stringOffset = c->length (); + + return_trace (true); + } + + bool subset (hb_subset_context_t *c) const + { + auto *name_prime = c->serializer->start_embed (); + +#ifdef HB_EXPERIMENTAL_API + const hb_hashmap_t *name_table_overrides = + &c->plan->name_table_overrides; +#endif + + auto it = + + nameRecordZ.as_array (count) + | hb_filter (c->plan->name_ids, &NameRecord::nameID) + | hb_filter (c->plan->name_languages, &NameRecord::languageID) + | hb_filter ([&] (const NameRecord& namerecord) { + return + (c->plan->flags & HB_SUBSET_FLAGS_NAME_LEGACY) + || namerecord.isUnicode (); + }) +#ifdef HB_EXPERIMENTAL_API + | hb_filter ([&] (const NameRecord& namerecord) { + if (name_table_overrides->is_empty ()) + return true; + hb_ot_name_record_ids_t rec_ids (namerecord.platformID, + namerecord.encodingID, + namerecord.languageID, + namerecord.nameID); + + hb_bytes_t *p; + if (name_table_overrides->has (rec_ids, &p) && + (*p).length == 0) + return false; + return true; + }) +#endif + ; + +#ifdef HB_EXPERIMENTAL_API + hb_hashmap_t retained_name_record_ids; + for (const NameRecord& rec : it) + { + hb_ot_name_record_ids_t rec_ids (rec.platformID, + rec.encodingID, + rec.languageID, + rec.nameID); + retained_name_record_ids.set (rec_ids, 1); + } + + hb_vector_t insert_name_records; + if (!name_table_overrides->is_empty ()) + { + if (unlikely (!insert_name_records.alloc (name_table_overrides->get_population (), true))) + return false; + for (const auto& record_ids : name_table_overrides->keys ()) + { + if (name_table_overrides->get (record_ids).length == 0) + continue; + if (retained_name_record_ids.has (record_ids)) + continue; + insert_name_records.push (record_ids); + } + } +#endif + + return name_prime->serialize (c->serializer, it, + std::addressof (this + stringOffset) +#ifdef HB_EXPERIMENTAL_API + , insert_name_records + , name_table_overrides +#endif + ); + } + + bool sanitize_records (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + const void *string_pool = (this+stringOffset).arrayZ; + return_trace (nameRecordZ.sanitize (c, count, string_pool)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + likely (format == 0 || format == 1) && + c->check_array (nameRecordZ.arrayZ, count) && + c->check_range (this, stringOffset) && + sanitize_records (c)); + } + + struct accelerator_t + { + accelerator_t (hb_face_t *face) + { + this->table = hb_sanitize_context_t ().reference_table (face); + assert (this->table.get_length () >= this->table->stringOffset); + this->pool = (const char *) (const void *) (this->table+this->table->stringOffset); + this->pool_len = this->table.get_length () - this->table->stringOffset; + const hb_array_t all_names (this->table->nameRecordZ.arrayZ, + this->table->count); + + this->names.alloc (all_names.length, true); + + for (unsigned int i = 0; i < all_names.length; i++) + { + hb_ot_name_entry_t *entry = this->names.push (); + + entry->name_id = all_names[i].nameID; + entry->language = all_names[i].language (face); + entry->entry_score = all_names[i].score (); + entry->entry_index = i; + } + + this->names.qsort (_hb_ot_name_entry_cmp); + /* Walk and pick best only for each name_id,language pair, + * while dropping unsupported encodings. */ + unsigned int j = 0; + for (unsigned int i = 0; i < this->names.length; i++) + { + if (this->names[i].entry_score == UNSUPPORTED || + this->names[i].language == HB_LANGUAGE_INVALID) + continue; + if (i && + this->names[i - 1].name_id == this->names[i].name_id && + this->names[i - 1].language == this->names[i].language) + continue; + this->names[j++] = this->names[i]; + } + this->names.resize (j); + } + ~accelerator_t () + { + this->table.destroy (); + } + + int get_index (hb_ot_name_id_t name_id, + hb_language_t language, + unsigned int *width=nullptr) const + { + const hb_ot_name_entry_t key = {name_id, {0}, language}; + const hb_ot_name_entry_t *entry = hb_bsearch (key, (const hb_ot_name_entry_t *) this->names, + this->names.length, + sizeof (hb_ot_name_entry_t), + _hb_ot_name_entry_cmp_key, + true); + + if (!entry) + { + entry = hb_bsearch (key, (const hb_ot_name_entry_t *) this->names, + this->names.length, + sizeof (hb_ot_name_entry_t), + _hb_ot_name_entry_cmp_key, + false); + } + + if (!entry) + return -1; + + if (width) + *width = entry->entry_score < 10 ? 2 : 1; + + return entry->entry_index; + } + + hb_bytes_t get_name (unsigned int idx) const + { + const hb_array_t all_names (table->nameRecordZ.arrayZ, table->count); + const NameRecord &record = all_names[idx]; + const hb_bytes_t string_pool (pool, pool_len); + return string_pool.sub_array (record.offset, record.length); + } + + private: + const char *pool; + unsigned int pool_len; + public: + hb_blob_ptr_t table; + hb_vector_t names; + }; + + public: + /* We only implement format 0 for now. */ + HBUINT16 format; /* Format selector (=0/1). */ + HBUINT16 count; /* Number of name records. */ + NNOffset16To> + stringOffset; /* Offset to start of string storage (from start of table). */ + UnsizedArrayOf + nameRecordZ; /* The name records where count is the number of records. */ + public: + DEFINE_SIZE_ARRAY (6, nameRecordZ); +}; + +#undef entry_index +#undef entry_score + +struct name_accelerator_t : name::accelerator_t { + name_accelerator_t (hb_face_t *face) : name::accelerator_t (face) {} +}; + +} /* namespace OT */ + + +#endif /* OT_NAME_NAME_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/graph/classdef-graph.hh b/src/3rdparty/harfbuzz-ng/src/graph/classdef-graph.hh new file mode 100644 index 00000000000..c1432883ffa --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/graph/classdef-graph.hh @@ -0,0 +1,222 @@ +/* + * Copyright © 2022 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 "graph.hh" +#include "../hb-ot-layout-common.hh" + +#ifndef GRAPH_CLASSDEF_GRAPH_HH +#define GRAPH_CLASSDEF_GRAPH_HH + +namespace graph { + +struct ClassDefFormat1 : public OT::ClassDefFormat1_3 +{ + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + constexpr unsigned min_size = OT::ClassDefFormat1_3::min_size; + if (vertex_len < min_size) return false; + return vertex_len >= min_size + classValue.get_size () - classValue.len.get_size (); + } +}; + +struct ClassDefFormat2 : public OT::ClassDefFormat2_4 +{ + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + constexpr unsigned min_size = OT::ClassDefFormat2_4::min_size; + if (vertex_len < min_size) return false; + return vertex_len >= min_size + rangeRecord.get_size () - rangeRecord.len.get_size (); + } +}; + +struct ClassDef : public OT::ClassDef +{ + template + static bool add_class_def (gsubgpos_graph_context_t& c, + unsigned parent_id, + unsigned link_position, + It glyph_and_class, + unsigned max_size) + { + unsigned class_def_prime_id = c.graph.new_node (nullptr, nullptr); + auto& class_def_prime_vertex = c.graph.vertices_[class_def_prime_id]; + if (!make_class_def (c, glyph_and_class, class_def_prime_id, max_size)) + return false; + + auto* class_def_link = c.graph.vertices_[parent_id].obj.real_links.push (); + class_def_link->width = SmallTypes::size; + class_def_link->objidx = class_def_prime_id; + class_def_link->position = link_position; + class_def_prime_vertex.add_parent (parent_id); + + return true; + } + + template + static bool make_class_def (gsubgpos_graph_context_t& c, + It glyph_and_class, + unsigned dest_obj, + unsigned max_size) + { + char* buffer = (char*) hb_calloc (1, max_size); + hb_serialize_context_t serializer (buffer, max_size); + OT::ClassDef_serialize (&serializer, glyph_and_class); + serializer.end_serialize (); + if (serializer.in_error ()) + { + hb_free (buffer); + return false; + } + + hb_bytes_t class_def_copy = serializer.copy_bytes (); + if (!class_def_copy.arrayZ) return false; + // Give ownership to the context, it will cleanup the buffer. + if (!c.add_buffer ((char *) class_def_copy.arrayZ)) + { + hb_free ((char *) class_def_copy.arrayZ); + return false; + } + + auto& obj = c.graph.vertices_[dest_obj].obj; + obj.head = (char *) class_def_copy.arrayZ; + obj.tail = obj.head + class_def_copy.length; + + hb_free (buffer); + return true; + } + + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + if (vertex_len < OT::ClassDef::min_size) return false; + switch (u.format) + { + case 1: return ((ClassDefFormat1*)this)->sanitize (vertex); + case 2: return ((ClassDefFormat2*)this)->sanitize (vertex); +#ifndef HB_NO_BEYOND_64K + // Not currently supported + case 3: + case 4: +#endif + default: return false; + } + } +}; + + +struct class_def_size_estimator_t +{ + template + class_def_size_estimator_t (It glyph_and_class) + : gids_consecutive (true), num_ranges_per_class (), glyphs_per_class () + { + unsigned last_gid = (unsigned) -1; + for (auto p : + glyph_and_class) + { + unsigned gid = p.first; + unsigned klass = p.second; + + if (last_gid != (unsigned) -1 && gid != last_gid + 1) + gids_consecutive = false; + last_gid = gid; + + hb_set_t* glyphs; + if (glyphs_per_class.has (klass, &glyphs) && glyphs) { + glyphs->add (gid); + continue; + } + + hb_set_t new_glyphs; + new_glyphs.add (gid); + glyphs_per_class.set (klass, std::move (new_glyphs)); + } + + if (in_error ()) return; + + for (unsigned klass : glyphs_per_class.keys ()) + { + if (!klass) continue; // class 0 doesn't get encoded. + + const hb_set_t& glyphs = glyphs_per_class.get (klass); + hb_codepoint_t start = HB_SET_VALUE_INVALID; + hb_codepoint_t end = HB_SET_VALUE_INVALID; + + unsigned count = 0; + while (glyphs.next_range (&start, &end)) + count++; + + num_ranges_per_class.set (klass, count); + } + } + + // Incremental increase in the Coverage and ClassDef table size + // (worst case) if all glyphs associated with 'klass' were added. + unsigned incremental_coverage_size (unsigned klass) const + { + // Coverage takes 2 bytes per glyph worst case, + return 2 * glyphs_per_class.get (klass).get_population (); + } + + // Incremental increase in the Coverage and ClassDef table size + // (worst case) if all glyphs associated with 'klass' were added. + unsigned incremental_class_def_size (unsigned klass) const + { + // ClassDef takes 6 bytes per range + unsigned class_def_2_size = 6 * num_ranges_per_class.get (klass); + if (gids_consecutive) + { + // ClassDef1 takes 2 bytes per glyph, but only can be used + // when gids are consecutive. + return hb_min (2 * glyphs_per_class.get (klass).get_population (), class_def_2_size); + } + + return class_def_2_size; + } + + bool in_error () + { + if (num_ranges_per_class.in_error ()) return true; + if (glyphs_per_class.in_error ()) return true; + + for (const hb_set_t& s : glyphs_per_class.values ()) + { + if (s.in_error ()) return true; + } + return false; + } + + private: + bool gids_consecutive; + hb_hashmap_t num_ranges_per_class; + hb_hashmap_t glyphs_per_class; +}; + + +} + +#endif // GRAPH_CLASSDEF_GRAPH_HH diff --git a/src/3rdparty/harfbuzz-ng/src/graph/coverage-graph.hh b/src/3rdparty/harfbuzz-ng/src/graph/coverage-graph.hh new file mode 100644 index 00000000000..4f44e076d1f --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/graph/coverage-graph.hh @@ -0,0 +1,158 @@ +/* + * Copyright © 2022 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 "graph.hh" +#include "../OT/Layout/Common/Coverage.hh" + +#ifndef GRAPH_COVERAGE_GRAPH_HH +#define GRAPH_COVERAGE_GRAPH_HH + +namespace graph { + +struct CoverageFormat1 : public OT::Layout::Common::CoverageFormat1_3 +{ + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + constexpr unsigned min_size = OT::Layout::Common::CoverageFormat1_3::min_size; + if (vertex_len < min_size) return false; + return vertex_len >= min_size + glyphArray.get_size () - glyphArray.len.get_size (); + } +}; + +struct CoverageFormat2 : public OT::Layout::Common::CoverageFormat2_4 +{ + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + constexpr unsigned min_size = OT::Layout::Common::CoverageFormat2_4::min_size; + if (vertex_len < min_size) return false; + return vertex_len >= min_size + rangeRecord.get_size () - rangeRecord.len.get_size (); + } +}; + +struct Coverage : public OT::Layout::Common::Coverage +{ + static Coverage* clone_coverage (gsubgpos_graph_context_t& c, + unsigned coverage_id, + unsigned new_parent_id, + unsigned link_position, + unsigned start, unsigned end) + + { + unsigned coverage_size = c.graph.vertices_[coverage_id].table_size (); + auto& coverage_v = c.graph.vertices_[coverage_id]; + Coverage* coverage_table = (Coverage*) coverage_v.obj.head; + if (!coverage_table || !coverage_table->sanitize (coverage_v)) + return nullptr; + + auto new_coverage = + + hb_zip (coverage_table->iter (), hb_range ()) + | hb_filter ([&] (hb_pair_t p) { + return p.second >= start && p.second < end; + }) + | hb_map_retains_sorting (hb_first) + ; + + return add_coverage (c, new_parent_id, link_position, new_coverage, coverage_size); + } + + template + static Coverage* add_coverage (gsubgpos_graph_context_t& c, + unsigned parent_id, + unsigned link_position, + It glyphs, + unsigned max_size) + { + unsigned coverage_prime_id = c.graph.new_node (nullptr, nullptr); + auto& coverage_prime_vertex = c.graph.vertices_[coverage_prime_id]; + if (!make_coverage (c, glyphs, coverage_prime_id, max_size)) + return nullptr; + + auto* coverage_link = c.graph.vertices_[parent_id].obj.real_links.push (); + coverage_link->width = SmallTypes::size; + coverage_link->objidx = coverage_prime_id; + coverage_link->position = link_position; + coverage_prime_vertex.add_parent (parent_id); + + return (Coverage*) coverage_prime_vertex.obj.head; + } + + template + static bool make_coverage (gsubgpos_graph_context_t& c, + It glyphs, + unsigned dest_obj, + unsigned max_size) + { + char* buffer = (char*) hb_calloc (1, max_size); + hb_serialize_context_t serializer (buffer, max_size); + OT::Layout::Common::Coverage_serialize (&serializer, glyphs); + serializer.end_serialize (); + if (serializer.in_error ()) + { + hb_free (buffer); + return false; + } + + hb_bytes_t coverage_copy = serializer.copy_bytes (); + if (!coverage_copy.arrayZ) return false; + // Give ownership to the context, it will cleanup the buffer. + if (!c.add_buffer ((char *) coverage_copy.arrayZ)) + { + hb_free ((char *) coverage_copy.arrayZ); + return false; + } + + auto& obj = c.graph.vertices_[dest_obj].obj; + obj.head = (char *) coverage_copy.arrayZ; + obj.tail = obj.head + coverage_copy.length; + + hb_free (buffer); + return true; + } + + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + if (vertex_len < OT::Layout::Common::Coverage::min_size) return false; + switch (u.format) + { + case 1: return ((CoverageFormat1*)this)->sanitize (vertex); + case 2: return ((CoverageFormat2*)this)->sanitize (vertex); +#ifndef HB_NO_BEYOND_64K + // Not currently supported + case 3: + case 4: +#endif + default: return false; + } + } +}; + + +} + +#endif // GRAPH_COVERAGE_GRAPH_HH diff --git a/src/3rdparty/harfbuzz-ng/src/graph/graph.hh b/src/3rdparty/harfbuzz-ng/src/graph/graph.hh new file mode 100644 index 00000000000..2b4e1b2d3f3 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/graph/graph.hh @@ -0,0 +1,1517 @@ +/* + * Copyright © 2022 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-set.hh" +#include "../hb-priority-queue.hh" +#include "../hb-serialize.hh" + +#ifndef GRAPH_GRAPH_HH +#define GRAPH_GRAPH_HH + +namespace graph { + +/** + * Represents a serialized table in the form of a graph. + * Provides methods for modifying and reordering the graph. + */ +struct graph_t +{ + struct vertex_t + { + hb_serialize_context_t::object_t obj; + int64_t distance = 0 ; + unsigned space = 0 ; + unsigned start = 0; + unsigned end = 0; + unsigned priority = 0; + private: + unsigned incoming_edges_ = 0; + unsigned single_parent = (unsigned) -1; + hb_hashmap_t parents; + public: + + auto parents_iter () const HB_AUTO_RETURN + ( + hb_concat ( + hb_iter (&single_parent, single_parent != (unsigned) -1), + parents.keys_ref () + ) + ) + + bool in_error () const + { + return parents.in_error (); + } + + bool link_positions_valid (unsigned num_objects, bool removed_nil) + { + hb_set_t assigned_bytes; + for (const auto& l : obj.real_links) + { + if (l.objidx >= num_objects + || (removed_nil && !l.objidx)) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + "Invalid graph. Invalid object index."); + return false; + } + + unsigned start = l.position; + unsigned end = start + l.width - 1; + + if (unlikely (l.width < 2 || l.width > 4)) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + "Invalid graph. Invalid link width."); + return false; + } + + if (unlikely (end >= table_size ())) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + "Invalid graph. Link position is out of bounds."); + return false; + } + + if (unlikely (assigned_bytes.intersects (start, end))) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + "Invalid graph. Found offsets whose positions overlap."); + return false; + } + + assigned_bytes.add_range (start, end); + } + + return !assigned_bytes.in_error (); + } + + void normalize () + { + obj.real_links.qsort (); + for (auto& l : obj.real_links) + { + for (unsigned i = 0; i < l.width; i++) + { + obj.head[l.position + i] = 0; + } + } + } + + bool equals (const vertex_t& other, + const graph_t& graph, + const graph_t& other_graph, + unsigned depth) const + { + if (!(as_bytes () == other.as_bytes ())) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + "vertex [%lu] bytes != [%lu] bytes, depth = %u", + (unsigned long) table_size (), + (unsigned long) other.table_size (), + depth); + + auto a = as_bytes (); + auto b = other.as_bytes (); + while (a || b) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + " 0x%x %s 0x%x", (unsigned) *a, (*a == *b) ? "==" : "!=", (unsigned) *b); + a++; + b++; + } + return false; + } + + return links_equal (obj.real_links, other.obj.real_links, graph, other_graph, depth); + } + + hb_bytes_t as_bytes () const + { + return hb_bytes_t (obj.head, table_size ()); + } + + friend void swap (vertex_t& a, vertex_t& b) + { + hb_swap (a.obj, b.obj); + hb_swap (a.distance, b.distance); + hb_swap (a.space, b.space); + hb_swap (a.single_parent, b.single_parent); + hb_swap (a.parents, b.parents); + hb_swap (a.incoming_edges_, b.incoming_edges_); + hb_swap (a.start, b.start); + hb_swap (a.end, b.end); + hb_swap (a.priority, b.priority); + } + + hb_hashmap_t + position_to_index_map () const + { + hb_hashmap_t result; + + result.alloc (obj.real_links.length); + for (const auto& l : obj.real_links) { + result.set (l.position, l.objidx); + } + + return result; + } + + bool is_shared () const + { + return parents.get_population () > 1; + } + + unsigned incoming_edges () const + { + if (HB_DEBUG_SUBSET_REPACK) + { + assert (incoming_edges_ == (single_parent != (unsigned) -1) + + (parents.values_ref () | hb_reduce (hb_add, 0))); + } + return incoming_edges_; + } + + void reset_parents () + { + incoming_edges_ = 0; + single_parent = (unsigned) -1; + parents.reset (); + } + + void add_parent (unsigned parent_index) + { + assert (parent_index != (unsigned) -1); + if (incoming_edges_ == 0) + { + single_parent = parent_index; + incoming_edges_ = 1; + return; + } + else if (single_parent != (unsigned) -1) + { + assert (incoming_edges_ == 1); + if (!parents.set (single_parent, 1)) + return; + single_parent = (unsigned) -1; + } + + unsigned *v; + if (parents.has (parent_index, &v)) + { + (*v)++; + incoming_edges_++; + } + else if (parents.set (parent_index, 1)) + incoming_edges_++; + } + + void remove_parent (unsigned parent_index) + { + if (parent_index == single_parent) + { + single_parent = (unsigned) -1; + incoming_edges_--; + return; + } + + unsigned *v; + if (parents.has (parent_index, &v)) + { + incoming_edges_--; + if (*v > 1) + (*v)--; + else + parents.del (parent_index); + + if (incoming_edges_ == 1) + { + single_parent = *parents.keys (); + parents.reset (); + } + } + } + + void remove_real_link (unsigned child_index, const void* offset) + { + unsigned count = obj.real_links.length; + for (unsigned i = 0; i < count; i++) + { + auto& link = obj.real_links.arrayZ[i]; + if (link.objidx != child_index) + continue; + + if ((obj.head + link.position) != offset) + continue; + + obj.real_links.remove_unordered (i); + return; + } + } + + bool remap_parents (const hb_vector_t& id_map) + { + if (single_parent != (unsigned) -1) + { + assert (single_parent < id_map.length); + single_parent = id_map[single_parent]; + return true; + } + + hb_hashmap_t new_parents; + new_parents.alloc (parents.get_population ()); + for (auto _ : parents) + { + assert (_.first < id_map.length); + assert (!new_parents.has (id_map[_.first])); + new_parents.set (id_map[_.first], _.second); + } + + if (parents.in_error() || new_parents.in_error ()) + return false; + + parents = std::move (new_parents); + return true; + } + + void remap_parent (unsigned old_index, unsigned new_index) + { + if (single_parent != (unsigned) -1) + { + if (single_parent == old_index) + single_parent = new_index; + return; + } + + const unsigned *pv; + if (parents.has (old_index, &pv)) + { + unsigned v = *pv; + if (!parents.set (new_index, v)) + incoming_edges_ -= v; + parents.del (old_index); + + if (incoming_edges_ == 1) + { + single_parent = *parents.keys (); + parents.reset (); + } + } + } + + bool is_leaf () const + { + return !obj.real_links.length && !obj.virtual_links.length; + } + + bool raise_priority () + { + if (has_max_priority ()) return false; + priority++; + return true; + } + + bool has_max_priority () const { + return priority >= 3; + } + + size_t table_size () const { + return obj.tail - obj.head; + } + + int64_t modified_distance (unsigned order) const + { + // TODO(garretrieger): once priority is high enough, should try + // setting distance = 0 which will force to sort immediately after + // it's parent where possible. + + int64_t modified_distance = + hb_min (hb_max(distance + distance_modifier (), 0), 0x7FFFFFFFFFF); + if (has_max_priority ()) { + modified_distance = 0; + } + return (modified_distance << 18) | (0x003FFFF & order); + } + + int64_t distance_modifier () const + { + if (!priority) return 0; + int64_t table_size = obj.tail - obj.head; + + if (priority == 1) + return -table_size / 2; + + return -table_size; + } + + private: + bool links_equal (const hb_vector_t& this_links, + const hb_vector_t& other_links, + const graph_t& graph, + const graph_t& other_graph, + unsigned depth) const + { + auto a = this_links.iter (); + auto b = other_links.iter (); + + while (a && b) + { + const auto& link_a = *a; + const auto& link_b = *b; + + if (link_a.width != link_b.width || + link_a.is_signed != link_b.is_signed || + link_a.whence != link_b.whence || + link_a.position != link_b.position || + link_a.bias != link_b.bias) + return false; + + if (!graph.vertices_[link_a.objidx].equals ( + other_graph.vertices_[link_b.objidx], graph, other_graph, depth + 1)) + return false; + + a++; + b++; + } + + if (bool (a) != bool (b)) + return false; + + return true; + } + }; + + template + struct vertex_and_table_t + { + vertex_and_table_t () : index (0), vertex (nullptr), table (nullptr) + {} + + unsigned index; + vertex_t* vertex; + T* table; + + operator bool () { + return table && vertex; + } + }; + + /* + * A topological sorting of an object graph. Ordered + * in reverse serialization order (first object in the + * serialization is at the end of the list). This matches + * the 'packed' object stack used internally in the + * serializer + */ + template + graph_t (const T& objects) + : parents_invalid (true), + distance_invalid (true), + positions_invalid (true), + successful (true), + buffers () + { + num_roots_for_space_.push (1); + bool removed_nil = false; + vertices_.alloc (objects.length); + vertices_scratch_.alloc (objects.length); + unsigned count = objects.length; + for (unsigned i = 0; i < count; i++) + { + // If this graph came from a serialization buffer object 0 is the + // nil object. We don't need it for our purposes here so drop it. + if (i == 0 && !objects.arrayZ[i]) + { + removed_nil = true; + continue; + } + + vertex_t* v = vertices_.push (); + if (check_success (!vertices_.in_error ())) + v->obj = *objects.arrayZ[i]; + + check_success (v->link_positions_valid (count, removed_nil)); + + if (!removed_nil) continue; + // Fix indices to account for removed nil object. + for (auto& l : v->obj.all_links_writer ()) { + l.objidx--; + } + } + } + + ~graph_t () + { + for (char* b : buffers) + hb_free (b); + } + + bool operator== (const graph_t& other) const + { + return root ().equals (other.root (), *this, other, 0); + } + + void print () const { + for (int i = vertices_.length - 1; i >= 0; i--) + { + const auto& v = vertices_[i]; + printf("%d: %u [", i, (unsigned int)v.table_size()); + for (const auto &l : v.obj.real_links) { + printf("%u, ", l.objidx); + } + printf("]\n"); + } + } + + // Sorts links of all objects in a consistent manner and zeroes all offsets. + void normalize () + { + for (auto& v : vertices_.writer ()) + v.normalize (); + } + + bool in_error () const + { + return !successful || + vertices_.in_error () || + num_roots_for_space_.in_error (); + } + + const vertex_t& root () const + { + return vertices_[root_idx ()]; + } + + unsigned root_idx () const + { + // Object graphs are in reverse order, the first object is at the end + // of the vector. Since the graph is topologically sorted it's safe to + // assume the first object has no incoming edges. + return vertices_.length - 1; + } + + const hb_serialize_context_t::object_t& object (unsigned i) const + { + return vertices_[i].obj; + } + + bool add_buffer (char* buffer) + { + buffers.push (buffer); + return !buffers.in_error (); + } + + /* + * Adds a 16 bit link from parent_id to child_id + */ + template + void add_link (T* offset, + unsigned parent_id, + unsigned child_id) + { + auto& v = vertices_[parent_id]; + auto* link = v.obj.real_links.push (); + link->width = 2; + link->objidx = child_id; + link->position = (char*) offset - (char*) v.obj.head; + vertices_[child_id].add_parent (parent_id); + } + + /* + * Generates a new topological sorting of graph ordered by the shortest + * distance to each node if positions are marked as invalid. + */ + void sort_shortest_distance_if_needed () + { + if (!positions_invalid) return; + sort_shortest_distance (); + } + + + /* + * Generates a new topological sorting of graph ordered by the shortest + * distance to each node. + */ + void sort_shortest_distance () + { + positions_invalid = true; + + if (vertices_.length <= 1) { + // Graph of 1 or less doesn't need sorting. + return; + } + + update_distances (); + + hb_priority_queue_t queue; + hb_vector_t &sorted_graph = vertices_scratch_; + if (unlikely (!check_success (sorted_graph.resize (vertices_.length)))) return; + hb_vector_t id_map; + if (unlikely (!check_success (id_map.resize (vertices_.length)))) return; + + hb_vector_t removed_edges; + if (unlikely (!check_success (removed_edges.resize (vertices_.length)))) return; + update_parents (); + + queue.insert (root ().modified_distance (0), root_idx ()); + int new_id = root_idx (); + unsigned order = 1; + while (!queue.in_error () && !queue.is_empty ()) + { + unsigned next_id = queue.pop_minimum().second; + + sorted_graph[new_id] = std::move (vertices_[next_id]); + const vertex_t& next = sorted_graph[new_id]; + + if (unlikely (!check_success(new_id >= 0))) { + // We are out of ids. Which means we've visited a node more than once. + // This graph contains a cycle which is not allowed. + DEBUG_MSG (SUBSET_REPACK, nullptr, "Invalid graph. Contains cycle."); + return; + } + + id_map[next_id] = new_id--; + + for (const auto& link : next.obj.all_links ()) { + removed_edges[link.objidx]++; + if (!(vertices_[link.objidx].incoming_edges () - removed_edges[link.objidx])) + // Add the order that the links were encountered to the priority. + // This ensures that ties between priorities objects are broken in a consistent + // way. More specifically this is set up so that if a set of objects have the same + // distance they'll be added to the topological order in the order that they are + // referenced from the parent object. + queue.insert (vertices_[link.objidx].modified_distance (order++), + link.objidx); + } + } + + check_success (!queue.in_error ()); + check_success (!sorted_graph.in_error ()); + + check_success (remap_all_obj_indices (id_map, &sorted_graph)); + vertices_ = std::move (sorted_graph); + + if (!check_success (new_id == -1)) + print_orphaned_nodes (); + } + + /* + * Finds the set of nodes (placed into roots) that should be assigned unique spaces. + * More specifically this looks for the top most 24 bit or 32 bit links in the graph. + * Some special casing is done that is specific to the layout of GSUB/GPOS tables. + */ + void find_space_roots (hb_set_t& visited, hb_set_t& roots) + { + int root_index = (int) root_idx (); + for (int i = root_index; i >= 0; i--) + { + if (visited.has (i)) continue; + + // Only real links can form 32 bit spaces + for (auto& l : vertices_[i].obj.real_links) + { + if (l.is_signed || l.width < 3) + continue; + + if (i == root_index && l.width == 3) + // Ignore 24bit links from the root node, this skips past the single 24bit + // pointer to the lookup list. + continue; + + if (l.width == 3) + { + // A 24bit offset forms a root, unless there is 32bit offsets somewhere + // in it's subgraph, then those become the roots instead. This is to make sure + // that extension subtables beneath a 24bit lookup become the spaces instead + // of the offset to the lookup. + hb_set_t sub_roots; + find_32bit_roots (l.objidx, sub_roots); + if (sub_roots) { + for (unsigned sub_root_idx : sub_roots) { + roots.add (sub_root_idx); + find_subgraph (sub_root_idx, visited); + } + continue; + } + } + + roots.add (l.objidx); + find_subgraph (l.objidx, visited); + } + } + } + + template + vertex_and_table_t as_table (unsigned parent, const void* offset, Ts... ds) + { + return as_table_from_index (index_for_offset (parent, offset), std::forward(ds)...); + } + + template + vertex_and_table_t as_mutable_table (unsigned parent, const void* offset, Ts... ds) + { + return as_table_from_index (mutable_index_for_offset (parent, offset), std::forward(ds)...); + } + + template + vertex_and_table_t as_table_from_index (unsigned index, Ts... ds) + { + if (index >= vertices_.length) + return vertex_and_table_t (); + + vertex_and_table_t r; + r.vertex = &vertices_[index]; + r.table = (T*) r.vertex->obj.head; + r.index = index; + if (!r.table) + return vertex_and_table_t (); + + if (!r.table->sanitize (*(r.vertex), std::forward(ds)...)) + return vertex_and_table_t (); + + return r; + } + + // Finds the object id of the object pointed to by the offset at 'offset' + // within object[node_idx]. + unsigned index_for_offset (unsigned node_idx, const void* offset) const + { + const auto& node = object (node_idx); + if (offset < node.head || offset >= node.tail) return -1; + + unsigned count = node.real_links.length; + for (unsigned i = 0; i < count; i++) + { + // Use direct access for increased performance, this is a hot method. + const auto& link = node.real_links.arrayZ[i]; + if (offset != node.head + link.position) + continue; + return link.objidx; + } + + return -1; + } + + // Finds the object id of the object pointed to by the offset at 'offset' + // within object[node_idx]. Ensures that the returned object is safe to mutate. + // That is, if the original child object is shared by parents other than node_idx + // it will be duplicated and the duplicate will be returned instead. + unsigned mutable_index_for_offset (unsigned node_idx, const void* offset) + { + unsigned child_idx = index_for_offset (node_idx, offset); + auto& child = vertices_[child_idx]; + for (unsigned p : child.parents_iter ()) + { + if (p != node_idx) { + return duplicate (node_idx, child_idx); + } + } + + return child_idx; + } + + + /* + * Assign unique space numbers to each connected subgraph of 24 bit and/or 32 bit offset(s). + * Currently, this is implemented specifically tailored to the structure of a GPOS/GSUB + * (including with 24bit offsets) table. + */ + bool assign_spaces () + { + update_parents (); + + hb_set_t visited; + hb_set_t roots; + find_space_roots (visited, roots); + + // Mark everything not in the subgraphs of the roots as visited. This prevents + // subgraphs from being connected via nodes not in those subgraphs. + visited.invert (); + + if (!roots) return false; + + while (roots) + { + uint32_t next = HB_SET_VALUE_INVALID; + if (unlikely (!check_success (!roots.in_error ()))) break; + if (!roots.next (&next)) break; + + hb_set_t connected_roots; + find_connected_nodes (next, roots, visited, connected_roots); + if (unlikely (!check_success (!connected_roots.in_error ()))) break; + + isolate_subgraph (connected_roots); + if (unlikely (!check_success (!connected_roots.in_error ()))) break; + + unsigned next_space = this->next_space (); + num_roots_for_space_.push (0); + for (unsigned root : connected_roots) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, "Subgraph %u gets space %u", root, next_space); + vertices_[root].space = next_space; + num_roots_for_space_[next_space] = num_roots_for_space_[next_space] + 1; + distance_invalid = true; + positions_invalid = true; + } + + // TODO(grieger): special case for GSUB/GPOS use extension promotions to move 16 bit space + // into the 32 bit space as needed, instead of using isolation. + } + + + + return true; + } + + /* + * Isolates the subgraph of nodes reachable from root. Any links to nodes in the subgraph + * that originate from outside of the subgraph will be removed by duplicating the linked to + * object. + * + * Indices stored in roots will be updated if any of the roots are duplicated to new indices. + */ + bool isolate_subgraph (hb_set_t& roots) + { + update_parents (); + hb_map_t subgraph; + + // incoming edges to root_idx should be all 32 bit in length so we don't need to de-dup these + // set the subgraph incoming edge count to match all of root_idx's incoming edges + hb_set_t parents; + for (unsigned root_idx : roots) + { + subgraph.set (root_idx, wide_parents (root_idx, parents)); + find_subgraph (root_idx, subgraph); + } + if (subgraph.in_error ()) + return false; + + unsigned original_root_idx = root_idx (); + hb_map_t index_map; + bool made_changes = false; + for (auto entry : subgraph.iter ()) + { + assert (entry.first < vertices_.length); + const auto& node = vertices_[entry.first]; + unsigned subgraph_incoming_edges = entry.second; + + if (subgraph_incoming_edges < node.incoming_edges ()) + { + // Only de-dup objects with incoming links from outside the subgraph. + made_changes = true; + duplicate_subgraph (entry.first, index_map); + } + } + + if (in_error ()) + return false; + + if (!made_changes) + return false; + + if (original_root_idx != root_idx () + && parents.has (original_root_idx)) + { + // If the root idx has changed since parents was determined, update root idx in parents + parents.add (root_idx ()); + parents.del (original_root_idx); + } + + auto new_subgraph = + + subgraph.keys () + | hb_map([&] (uint32_t node_idx) { + const uint32_t *v; + if (index_map.has (node_idx, &v)) return *v; + return node_idx; + }) + ; + + remap_obj_indices (index_map, new_subgraph); + remap_obj_indices (index_map, parents.iter (), true); + + // Update roots set with new indices as needed. + for (auto next : roots) + { + const uint32_t *v; + if (index_map.has (next, &v)) + { + roots.del (next); + roots.add (*v); + } + } + + return true; + } + + void find_subgraph (unsigned node_idx, hb_map_t& subgraph) + { + for (const auto& link : vertices_[node_idx].obj.all_links ()) + { + hb_codepoint_t *v; + if (subgraph.has (link.objidx, &v)) + { + (*v)++; + continue; + } + subgraph.set (link.objidx, 1); + find_subgraph (link.objidx, subgraph); + } + } + + void find_subgraph (unsigned node_idx, hb_set_t& subgraph) + { + if (subgraph.has (node_idx)) return; + subgraph.add (node_idx); + for (const auto& link : vertices_[node_idx].obj.all_links ()) + find_subgraph (link.objidx, subgraph); + } + + size_t find_subgraph_size (unsigned node_idx, hb_set_t& subgraph, unsigned max_depth = -1) + { + if (subgraph.has (node_idx)) return 0; + subgraph.add (node_idx); + + const auto& o = vertices_[node_idx].obj; + size_t size = o.tail - o.head; + if (max_depth == 0) + return size; + + for (const auto& link : o.all_links ()) + size += find_subgraph_size (link.objidx, subgraph, max_depth - 1); + return size; + } + + /* + * Finds the topmost children of 32bit offsets in the subgraph starting + * at node_idx. Found indices are placed into 'found'. + */ + void find_32bit_roots (unsigned node_idx, hb_set_t& found) + { + for (const auto& link : vertices_[node_idx].obj.all_links ()) + { + if (!link.is_signed && link.width == 4) { + found.add (link.objidx); + continue; + } + find_32bit_roots (link.objidx, found); + } + } + + /* + * Moves the child of old_parent_idx pointed to by old_offset to a new + * vertex at the new_offset. + */ + template + void move_child (unsigned old_parent_idx, + const O* old_offset, + unsigned new_parent_idx, + const O* new_offset) + { + distance_invalid = true; + positions_invalid = true; + + auto& old_v = vertices_[old_parent_idx]; + auto& new_v = vertices_[new_parent_idx]; + + unsigned child_id = index_for_offset (old_parent_idx, + old_offset); + + auto* new_link = new_v.obj.real_links.push (); + new_link->width = O::static_size; + new_link->objidx = child_id; + new_link->position = (const char*) new_offset - (const char*) new_v.obj.head; + + auto& child = vertices_[child_id]; + child.add_parent (new_parent_idx); + + old_v.remove_real_link (child_id, old_offset); + child.remove_parent (old_parent_idx); + } + + /* + * duplicates all nodes in the subgraph reachable from node_idx. Does not re-assign + * links. index_map is updated with mappings from old id to new id. If a duplication has already + * been performed for a given index, then it will be skipped. + */ + void duplicate_subgraph (unsigned node_idx, hb_map_t& index_map) + { + if (index_map.has (node_idx)) + return; + + unsigned clone_idx = duplicate (node_idx); + if (!check_success (clone_idx != (unsigned) -1)) + return; + + index_map.set (node_idx, clone_idx); + for (const auto& l : object (node_idx).all_links ()) { + duplicate_subgraph (l.objidx, index_map); + } + } + + /* + * Creates a copy of node_idx and returns it's new index. + */ + unsigned duplicate (unsigned node_idx) + { + positions_invalid = true; + distance_invalid = true; + + auto* clone = vertices_.push (); + auto& child = vertices_[node_idx]; + if (vertices_.in_error ()) { + return -1; + } + + clone->obj.head = child.obj.head; + clone->obj.tail = child.obj.tail; + clone->distance = child.distance; + clone->space = child.space; + clone->reset_parents (); + + unsigned clone_idx = vertices_.length - 2; + for (const auto& l : child.obj.real_links) + { + clone->obj.real_links.push (l); + vertices_[l.objidx].add_parent (clone_idx); + } + for (const auto& l : child.obj.virtual_links) + { + clone->obj.virtual_links.push (l); + vertices_[l.objidx].add_parent (clone_idx); + } + + check_success (!clone->obj.real_links.in_error ()); + check_success (!clone->obj.virtual_links.in_error ()); + + // The last object is the root of the graph, so swap back the root to the end. + // The root's obj idx does change, however since it's root nothing else refers to it. + // all other obj idx's will be unaffected. + hb_swap (vertices_[vertices_.length - 2], *clone); + + // Since the root moved, update the parents arrays of all children on the root. + for (const auto& l : root ().obj.all_links ()) + vertices_[l.objidx].remap_parent (root_idx () - 1, root_idx ()); + + return clone_idx; + } + + /* + * Creates a copy of child and re-assigns the link from + * parent to the clone. The copy is a shallow copy, objects + * linked from child are not duplicated. + */ + unsigned duplicate_if_shared (unsigned parent_idx, unsigned child_idx) + { + unsigned new_idx = duplicate (parent_idx, child_idx); + if (new_idx == (unsigned) -1) return child_idx; + return new_idx; + } + + + /* + * Creates a copy of child and re-assigns the link from + * parent to the clone. The copy is a shallow copy, objects + * linked from child are not duplicated. + */ + unsigned duplicate (unsigned parent_idx, unsigned child_idx) + { + update_parents (); + + unsigned links_to_child = 0; + for (const auto& l : vertices_[parent_idx].obj.all_links ()) + { + if (l.objidx == child_idx) links_to_child++; + } + + if (vertices_[child_idx].incoming_edges () <= links_to_child) + { + // Can't duplicate this node, doing so would orphan the original one as all remaining links + // to child are from parent. + DEBUG_MSG (SUBSET_REPACK, nullptr, " Not duplicating %u => %u", + parent_idx, child_idx); + return -1; + } + + DEBUG_MSG (SUBSET_REPACK, nullptr, " Duplicating %u => %u", + parent_idx, child_idx); + + unsigned clone_idx = duplicate (child_idx); + if (clone_idx == (unsigned) -1) return false; + // duplicate shifts the root node idx, so if parent_idx was root update it. + if (parent_idx == clone_idx) parent_idx++; + + auto& parent = vertices_[parent_idx]; + for (auto& l : parent.obj.all_links_writer ()) + { + if (l.objidx != child_idx) + continue; + + reassign_link (l, parent_idx, clone_idx); + } + + return clone_idx; + } + + + /* + * Adds a new node to the graph, not connected to anything. + */ + unsigned new_node (char* head, char* tail) + { + positions_invalid = true; + distance_invalid = true; + + auto* clone = vertices_.push (); + if (vertices_.in_error ()) { + return -1; + } + + clone->obj.head = head; + clone->obj.tail = tail; + clone->distance = 0; + clone->space = 0; + + unsigned clone_idx = vertices_.length - 2; + + // The last object is the root of the graph, so swap back the root to the end. + // The root's obj idx does change, however since it's root nothing else refers to it. + // all other obj idx's will be unaffected. + hb_swap (vertices_[vertices_.length - 2], *clone); + + // Since the root moved, update the parents arrays of all children on the root. + for (const auto& l : root ().obj.all_links ()) + vertices_[l.objidx].remap_parent (root_idx () - 1, root_idx ()); + + return clone_idx; + } + + /* + * Raises the sorting priority of all children. + */ + bool raise_childrens_priority (unsigned parent_idx) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, " Raising priority of all children of %u", + parent_idx); + // This operation doesn't change ordering until a sort is run, so no need + // to invalidate positions. It does not change graph structure so no need + // to update distances or edge counts. + auto& parent = vertices_[parent_idx].obj; + bool made_change = false; + for (auto& l : parent.all_links_writer ()) + made_change |= vertices_[l.objidx].raise_priority (); + return made_change; + } + + bool is_fully_connected () + { + update_parents(); + + if (root().incoming_edges ()) + // Root cannot have parents. + return false; + + for (unsigned i = 0; i < root_idx (); i++) + { + if (!vertices_[i].incoming_edges ()) + return false; + } + return true; + } + +#if 0 + /* + * Saves the current graph to a packed binary format which the repacker fuzzer takes + * as a seed. + */ + void save_fuzzer_seed (hb_tag_t tag) const + { + FILE* f = fopen ("./repacker_fuzzer_seed", "w"); + fwrite ((void*) &tag, sizeof (tag), 1, f); + + uint16_t num_objects = vertices_.length; + fwrite ((void*) &num_objects, sizeof (num_objects), 1, f); + + for (const auto& v : vertices_) + { + uint16_t blob_size = v.table_size (); + fwrite ((void*) &blob_size, sizeof (blob_size), 1, f); + fwrite ((const void*) v.obj.head, blob_size, 1, f); + } + + uint16_t link_count = 0; + for (const auto& v : vertices_) + link_count += v.obj.real_links.length; + + fwrite ((void*) &link_count, sizeof (link_count), 1, f); + + typedef struct + { + uint16_t parent; + uint16_t child; + uint16_t position; + uint8_t width; + } link_t; + + for (unsigned i = 0; i < vertices_.length; i++) + { + for (const auto& l : vertices_[i].obj.real_links) + { + link_t link { + (uint16_t) i, (uint16_t) l.objidx, + (uint16_t) l.position, (uint8_t) l.width + }; + fwrite ((void*) &link, sizeof (link), 1, f); + } + } + + fclose (f); + } +#endif + + void print_orphaned_nodes () + { + if (!DEBUG_ENABLED(SUBSET_REPACK)) return; + + DEBUG_MSG (SUBSET_REPACK, nullptr, "Graph is not fully connected."); + parents_invalid = true; + update_parents(); + + if (root().incoming_edges ()) { + DEBUG_MSG (SUBSET_REPACK, nullptr, "Root node has incoming edges."); + } + + for (unsigned i = 0; i < root_idx (); i++) + { + const auto& v = vertices_[i]; + if (!v.incoming_edges ()) + DEBUG_MSG (SUBSET_REPACK, nullptr, "Node %u is orphaned.", i); + } + } + + unsigned num_roots_for_space (unsigned space) const + { + return num_roots_for_space_[space]; + } + + unsigned next_space () const + { + return num_roots_for_space_.length; + } + + void move_to_new_space (const hb_set_t& indices) + { + num_roots_for_space_.push (0); + unsigned new_space = num_roots_for_space_.length - 1; + + for (unsigned index : indices) { + auto& node = vertices_[index]; + num_roots_for_space_[node.space] = num_roots_for_space_[node.space] - 1; + num_roots_for_space_[new_space] = num_roots_for_space_[new_space] + 1; + node.space = new_space; + distance_invalid = true; + positions_invalid = true; + } + } + + unsigned space_for (unsigned index, unsigned* root = nullptr) const + { + loop: + assert (index < vertices_.length); + const auto& node = vertices_[index]; + if (node.space) + { + if (root != nullptr) + *root = index; + return node.space; + } + + if (!node.incoming_edges ()) + { + if (root) + *root = index; + return 0; + } + + index = *node.parents_iter (); + goto loop; + } + + void err_other_error () { this->successful = false; } + + size_t total_size_in_bytes () const { + size_t total_size = 0; + unsigned count = vertices_.length; + for (unsigned i = 0; i < count; i++) { + size_t size = vertices_.arrayZ[i].obj.tail - vertices_.arrayZ[i].obj.head; + total_size += size; + } + return total_size; + } + + + private: + + /* + * Returns the numbers of incoming edges that are 24 or 32 bits wide. + */ + unsigned wide_parents (unsigned node_idx, hb_set_t& parents) const + { + unsigned count = 0; + for (unsigned p : vertices_[node_idx].parents_iter ()) + { + // Only real links can be wide + for (const auto& l : vertices_[p].obj.real_links) + { + if (l.objidx == node_idx + && (l.width == 3 || l.width == 4) + && !l.is_signed) + { + count++; + parents.add (p); + } + } + } + return count; + } + + bool check_success (bool success) + { return this->successful && (success || ((void) err_other_error (), false)); } + + public: + /* + * Creates a map from objid to # of incoming edges. + */ + void update_parents () + { + if (!parents_invalid) return; + + unsigned count = vertices_.length; + + for (unsigned i = 0; i < count; i++) + vertices_.arrayZ[i].reset_parents (); + + for (unsigned p = 0; p < count; p++) + { + for (auto& l : vertices_.arrayZ[p].obj.all_links ()) + vertices_[l.objidx].add_parent (p); + } + + for (unsigned i = 0; i < count; i++) + // parents arrays must be accurate or downstream operations like cycle detection + // and sorting won't work correctly. + check_success (!vertices_.arrayZ[i].in_error ()); + + parents_invalid = false; + } + + /* + * compute the serialized start and end positions for each vertex. + */ + void update_positions () + { + if (!positions_invalid) return; + + unsigned current_pos = 0; + for (int i = root_idx (); i >= 0; i--) + { + auto& v = vertices_[i]; + v.start = current_pos; + current_pos += v.obj.tail - v.obj.head; + v.end = current_pos; + } + + positions_invalid = false; + } + + /* + * Finds the distance to each object in the graph + * from the initial node. + */ + void update_distances () + { + if (!distance_invalid) return; + + // Uses Dijkstra's algorithm to find all of the shortest distances. + // https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm + // + // Implementation Note: + // Since our priority queue doesn't support fast priority decreases + // we instead just add new entries into the queue when a priority changes. + // Redundant ones are filtered out later on by the visited set. + // According to https://www3.cs.stonybrook.edu/~rezaul/papers/TR-07-54.pdf + // for practical performance this is faster then using a more advanced queue + // (such as a fibonacci queue) with a fast decrease priority. + unsigned count = vertices_.length; + for (unsigned i = 0; i < count; i++) + vertices_.arrayZ[i].distance = hb_int_max (int64_t); + vertices_.tail ().distance = 0; + + hb_priority_queue_t queue; + queue.insert (0, vertices_.length - 1); + + hb_vector_t visited; + visited.resize (vertices_.length); + + while (!queue.in_error () && !queue.is_empty ()) + { + unsigned next_idx = queue.pop_minimum ().second; + if (visited[next_idx]) continue; + const auto& next = vertices_[next_idx]; + int64_t next_distance = vertices_[next_idx].distance; + visited[next_idx] = true; + + for (const auto& link : next.obj.all_links ()) + { + if (visited[link.objidx]) continue; + + const auto& child = vertices_.arrayZ[link.objidx].obj; + unsigned link_width = link.width ? link.width : 4; // treat virtual offsets as 32 bits wide + int64_t child_weight = (child.tail - child.head) + + ((int64_t) 1 << (link_width * 8)) * (vertices_.arrayZ[link.objidx].space + 1); + int64_t child_distance = next_distance + child_weight; + + if (child_distance < vertices_.arrayZ[link.objidx].distance) + { + vertices_.arrayZ[link.objidx].distance = child_distance; + queue.insert (child_distance, link.objidx); + } + } + } + + check_success (!queue.in_error ()); + if (!check_success (queue.is_empty ())) + { + print_orphaned_nodes (); + return; + } + + distance_invalid = false; + } + + private: + /* + * Updates a link in the graph to point to a different object. Corrects the + * parents vector on the previous and new child nodes. + */ + void reassign_link (hb_serialize_context_t::object_t::link_t& link, + unsigned parent_idx, + unsigned new_idx) + { + unsigned old_idx = link.objidx; + link.objidx = new_idx; + vertices_[old_idx].remove_parent (parent_idx); + vertices_[new_idx].add_parent (parent_idx); + } + + /* + * Updates all objidx's in all links using the provided mapping. Corrects incoming edge counts. + */ + template + void remap_obj_indices (const hb_map_t& id_map, + Iterator subgraph, + bool only_wide = false) + { + if (!id_map) return; + for (unsigned i : subgraph) + { + for (auto& link : vertices_[i].obj.all_links_writer ()) + { + const uint32_t *v; + if (!id_map.has (link.objidx, &v)) continue; + if (only_wide && !(link.width == 4 && !link.is_signed)) continue; + + reassign_link (link, i, *v); + } + } + } + + /* + * Updates all objidx's in all links using the provided mapping. + */ + bool remap_all_obj_indices (const hb_vector_t& id_map, + hb_vector_t* sorted_graph) const + { + unsigned count = sorted_graph->length; + for (unsigned i = 0; i < count; i++) + { + if (!(*sorted_graph)[i].remap_parents (id_map)) + return false; + for (auto& link : sorted_graph->arrayZ[i].obj.all_links_writer ()) + { + link.objidx = id_map[link.objidx]; + } + } + return true; + } + + /* + * Finds all nodes in targets that are reachable from start_idx, nodes in visited will be skipped. + * For this search the graph is treated as being undirected. + * + * Connected targets will be added to connected and removed from targets. All visited nodes + * will be added to visited. + */ + void find_connected_nodes (unsigned start_idx, + hb_set_t& targets, + hb_set_t& visited, + hb_set_t& connected) + { + if (unlikely (!check_success (!visited.in_error ()))) return; + if (visited.has (start_idx)) return; + visited.add (start_idx); + + if (targets.has (start_idx)) + { + targets.del (start_idx); + connected.add (start_idx); + } + + const auto& v = vertices_[start_idx]; + + // Graph is treated as undirected so search children and parents of start_idx + for (const auto& l : v.obj.all_links ()) + find_connected_nodes (l.objidx, targets, visited, connected); + + for (unsigned p : v.parents_iter ()) + find_connected_nodes (p, targets, visited, connected); + } + + public: + // TODO(garretrieger): make private, will need to move most of offset overflow code into graph. + hb_vector_t vertices_; + hb_vector_t vertices_scratch_; + private: + bool parents_invalid; + bool distance_invalid; + bool positions_invalid; + bool successful; + hb_vector_t num_roots_for_space_; + hb_vector_t buffers; +}; + +} + +#endif // GRAPH_GRAPH_HH diff --git a/src/3rdparty/harfbuzz-ng/src/graph/gsubgpos-context.cc b/src/3rdparty/harfbuzz-ng/src/graph/gsubgpos-context.cc new file mode 100644 index 00000000000..d66eb49cfd2 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/graph/gsubgpos-context.cc @@ -0,0 +1,74 @@ +/* + * Copyright © 2022 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 "gsubgpos-graph.hh" + +namespace graph { + +gsubgpos_graph_context_t::gsubgpos_graph_context_t (hb_tag_t table_tag_, + graph_t& graph_) + : table_tag (table_tag_), + graph (graph_), + lookup_list_index (0), + lookups () +{ + if (table_tag_ != HB_OT_TAG_GPOS + && table_tag_ != HB_OT_TAG_GSUB) + return; + + GSTAR* gstar = graph::GSTAR::graph_to_gstar (graph_); + if (gstar) { + gstar->find_lookups (graph, lookups); + lookup_list_index = gstar->get_lookup_list_index (graph_); + } +} + +unsigned gsubgpos_graph_context_t::create_node (unsigned size) +{ + char* buffer = (char*) hb_calloc (1, size); + if (!buffer) + return -1; + + if (!add_buffer (buffer)) { + // Allocation did not get stored for freeing later. + hb_free (buffer); + return -1; + } + + return graph.new_node (buffer, buffer + size); +} + +unsigned gsubgpos_graph_context_t::num_non_ext_subtables () { + unsigned count = 0; + for (auto l : lookups.values ()) + { + if (l->is_extension (table_tag)) continue; + count += l->number_of_subtables (); + } + return count; +} + +} diff --git a/src/3rdparty/harfbuzz-ng/src/graph/gsubgpos-context.hh b/src/3rdparty/harfbuzz-ng/src/graph/gsubgpos-context.hh new file mode 100644 index 00000000000..b25d538fe3d --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/graph/gsubgpos-context.hh @@ -0,0 +1,61 @@ +/* + * Copyright © 2022 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 "graph.hh" +#include "../hb-ot-layout-gsubgpos.hh" + +#ifndef GRAPH_GSUBGPOS_CONTEXT_HH +#define GRAPH_GSUBGPOS_CONTEXT_HH + +namespace graph { + +struct Lookup; + +struct gsubgpos_graph_context_t +{ + hb_tag_t table_tag; + graph_t& graph; + unsigned lookup_list_index; + hb_hashmap_t lookups; + hb_hashmap_t subtable_to_extension; + + HB_INTERNAL gsubgpos_graph_context_t (hb_tag_t table_tag_, + graph_t& graph_); + + HB_INTERNAL unsigned create_node (unsigned size); + + bool add_buffer (char* buffer) + { + return graph.add_buffer (buffer); + } + + private: + HB_INTERNAL unsigned num_non_ext_subtables (); +}; + +} + +#endif // GRAPH_GSUBGPOS_CONTEXT diff --git a/src/3rdparty/harfbuzz-ng/src/graph/gsubgpos-graph.hh b/src/3rdparty/harfbuzz-ng/src/graph/gsubgpos-graph.hh new file mode 100644 index 00000000000..12fcbdc497f --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/graph/gsubgpos-graph.hh @@ -0,0 +1,431 @@ +/* + * Copyright © 2022 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 "graph.hh" +#include "../hb-ot-layout-gsubgpos.hh" +#include "../OT/Layout/GSUB/ExtensionSubst.hh" +#include "gsubgpos-context.hh" +#include "pairpos-graph.hh" +#include "markbasepos-graph.hh" + +#ifndef GRAPH_GSUBGPOS_GRAPH_HH +#define GRAPH_GSUBGPOS_GRAPH_HH + +namespace graph { + +struct Lookup; + +template +struct ExtensionFormat1 : public OT::ExtensionFormat1 +{ + void reset(unsigned type) + { + this->format = 1; + this->extensionLookupType = type; + this->extensionOffset = 0; + } + + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + return vertex_len >= OT::ExtensionFormat1::static_size; + } + + unsigned get_lookup_type () const + { + return this->extensionLookupType; + } + + unsigned get_subtable_index (graph_t& graph, unsigned this_index) const + { + return graph.index_for_offset (this_index, &this->extensionOffset); + } +}; + +struct Lookup : public OT::Lookup +{ + unsigned number_of_subtables () const + { + return subTable.len; + } + + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + if (vertex_len < OT::Lookup::min_size) return false; + return vertex_len >= this->get_size (); + } + + bool is_extension (hb_tag_t table_tag) const + { + return lookupType == extension_type (table_tag); + } + + bool make_extension (gsubgpos_graph_context_t& c, + unsigned this_index) + { + unsigned type = lookupType; + unsigned ext_type = extension_type (c.table_tag); + if (!ext_type || is_extension (c.table_tag)) + { + // NOOP + return true; + } + + DEBUG_MSG (SUBSET_REPACK, nullptr, + "Promoting lookup type %u (obj %u) to extension.", + type, + this_index); + + for (unsigned i = 0; i < subTable.len; i++) + { + unsigned subtable_index = c.graph.index_for_offset (this_index, &subTable[i]); + if (!make_subtable_extension (c, + this_index, + subtable_index)) + return false; + } + + lookupType = ext_type; + return true; + } + + bool split_subtables_if_needed (gsubgpos_graph_context_t& c, + unsigned this_index) + { + unsigned type = lookupType; + bool is_ext = is_extension (c.table_tag); + + if (c.table_tag != HB_OT_TAG_GPOS) + return true; + + if (!is_ext && + type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::Pair && + type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::MarkBase) + return true; + + hb_vector_t>> all_new_subtables; + for (unsigned i = 0; i < subTable.len; i++) + { + unsigned subtable_index = c.graph.index_for_offset (this_index, &subTable[i]); + unsigned parent_index = this_index; + if (is_ext) { + unsigned ext_subtable_index = subtable_index; + parent_index = ext_subtable_index; + ExtensionFormat1* extension = + (ExtensionFormat1*) + c.graph.object (ext_subtable_index).head; + if (!extension || !extension->sanitize (c.graph.vertices_[ext_subtable_index])) + continue; + + subtable_index = extension->get_subtable_index (c.graph, ext_subtable_index); + type = extension->get_lookup_type (); + if (type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::Pair + && type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::MarkBase) + continue; + } + + hb_vector_t new_sub_tables; + switch (type) + { + case 2: + new_sub_tables = split_subtable (c, parent_index, subtable_index); break; + case 4: + new_sub_tables = split_subtable (c, parent_index, subtable_index); break; + default: + break; + } + if (new_sub_tables.in_error ()) return false; + if (!new_sub_tables) continue; + hb_pair_t>* entry = all_new_subtables.push (); + entry->first = i; + entry->second = std::move (new_sub_tables); + } + + if (all_new_subtables) { + return add_sub_tables (c, this_index, type, all_new_subtables); + } + + return true; + } + + template + hb_vector_t split_subtable (gsubgpos_graph_context_t& c, + unsigned parent_idx, + unsigned objidx) + { + T* sub_table = (T*) c.graph.object (objidx).head; + if (!sub_table || !sub_table->sanitize (c.graph.vertices_[objidx])) + return hb_vector_t (); + + return sub_table->split_subtables (c, parent_idx, objidx); + } + + bool add_sub_tables (gsubgpos_graph_context_t& c, + unsigned this_index, + unsigned type, + hb_vector_t>>& subtable_ids) + { + bool is_ext = is_extension (c.table_tag); + auto& v = c.graph.vertices_[this_index]; + fix_existing_subtable_links (c, this_index, subtable_ids); + + unsigned new_subtable_count = 0; + for (const auto& p : subtable_ids) + new_subtable_count += p.second.length; + + size_t new_size = v.table_size () + + new_subtable_count * OT::Offset16::static_size; + char* buffer = (char*) hb_calloc (1, new_size); + if (!buffer) return false; + if (!c.add_buffer (buffer)) + { + hb_free (buffer); + return false; + } + hb_memcpy (buffer, v.obj.head, v.table_size()); + + v.obj.head = buffer; + v.obj.tail = buffer + new_size; + + Lookup* new_lookup = (Lookup*) buffer; + + unsigned shift = 0; + new_lookup->subTable.len = subTable.len + new_subtable_count; + for (const auto& p : subtable_ids) + { + unsigned offset_index = p.first + shift + 1; + shift += p.second.length; + + for (unsigned subtable_id : p.second) + { + if (is_ext) + { + unsigned ext_id = create_extension_subtable (c, subtable_id, type); + c.graph.vertices_[subtable_id].add_parent (ext_id); + subtable_id = ext_id; + } + + auto* link = v.obj.real_links.push (); + link->width = 2; + link->objidx = subtable_id; + link->position = (char*) &new_lookup->subTable[offset_index++] - + (char*) new_lookup; + c.graph.vertices_[subtable_id].add_parent (this_index); + } + } + + // Repacker sort order depends on link order, which we've messed up so resort it. + v.obj.real_links.qsort (); + + // The head location of the lookup has changed, invalidating the lookups map entry + // in the context. Update the map. + c.lookups.set (this_index, new_lookup); + return true; + } + + void fix_existing_subtable_links (gsubgpos_graph_context_t& c, + unsigned this_index, + hb_vector_t>>& subtable_ids) + { + auto& v = c.graph.vertices_[this_index]; + Lookup* lookup = (Lookup*) v.obj.head; + + unsigned shift = 0; + for (const auto& p : subtable_ids) + { + unsigned insert_index = p.first + shift; + unsigned pos_offset = p.second.length * OT::Offset16::static_size; + unsigned insert_offset = (char*) &lookup->subTable[insert_index] - (char*) lookup; + shift += p.second.length; + + for (auto& l : v.obj.all_links_writer ()) + { + if (l.position > insert_offset) l.position += pos_offset; + } + } + } + + unsigned create_extension_subtable (gsubgpos_graph_context_t& c, + unsigned subtable_index, + unsigned type) + { + unsigned extension_size = OT::ExtensionFormat1::static_size; + + unsigned ext_index = c.create_node (extension_size); + if (ext_index == (unsigned) -1) + return -1; + + auto& ext_vertex = c.graph.vertices_[ext_index]; + ExtensionFormat1* extension = + (ExtensionFormat1*) ext_vertex.obj.head; + extension->reset (type); + + // Make extension point at the subtable. + auto* l = ext_vertex.obj.real_links.push (); + + l->width = 4; + l->objidx = subtable_index; + l->position = 4; + + return ext_index; + } + + bool make_subtable_extension (gsubgpos_graph_context_t& c, + unsigned lookup_index, + unsigned subtable_index) + { + unsigned type = lookupType; + unsigned ext_index = -1; + unsigned* existing_ext_index = nullptr; + if (c.subtable_to_extension.has(subtable_index, &existing_ext_index)) { + ext_index = *existing_ext_index; + } else { + ext_index = create_extension_subtable(c, subtable_index, type); + c.subtable_to_extension.set(subtable_index, ext_index); + } + + if (ext_index == (unsigned) -1) + return false; + + auto& subtable_vertex = c.graph.vertices_[subtable_index]; + auto& lookup_vertex = c.graph.vertices_[lookup_index]; + for (auto& l : lookup_vertex.obj.real_links.writer ()) + { + if (l.objidx == subtable_index) { + // Change lookup to point at the extension. + l.objidx = ext_index; + if (existing_ext_index) + subtable_vertex.remove_parent(lookup_index); + } + } + + // Make extension point at the subtable. + auto& ext_vertex = c.graph.vertices_[ext_index]; + ext_vertex.add_parent (lookup_index); + if (!existing_ext_index) + subtable_vertex.remap_parent (lookup_index, ext_index); + + return true; + } + + private: + unsigned extension_type (hb_tag_t table_tag) const + { + switch (table_tag) + { + case HB_OT_TAG_GPOS: return 9; + case HB_OT_TAG_GSUB: return 7; + default: return 0; + } + } +}; + +template +struct LookupList : public OT::LookupList +{ + bool sanitize (const graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + if (vertex_len < OT::LookupList::min_size) return false; + return vertex_len >= OT::LookupList::item_size * this->len; + } +}; + +struct GSTAR : public OT::GSUBGPOS +{ + static GSTAR* graph_to_gstar (graph_t& graph) + { + const auto& r = graph.root (); + + GSTAR* gstar = (GSTAR*) r.obj.head; + if (!gstar || !gstar->sanitize (r)) + return nullptr; + + return gstar; + } + + const void* get_lookup_list_field_offset () const + { + switch (u.version.major) { + case 1: return u.version1.get_lookup_list_offset (); +#ifndef HB_NO_BEYOND_64K + case 2: return u.version2.get_lookup_list_offset (); +#endif + default: return 0; + } + } + + bool sanitize (const graph_t::vertex_t& vertex) + { + int64_t len = vertex.obj.tail - vertex.obj.head; + if (len < OT::GSUBGPOS::min_size) return false; + return len >= get_size (); + } + + void find_lookups (graph_t& graph, + hb_hashmap_t& lookups /* OUT */) + { + switch (u.version.major) { + case 1: find_lookups (graph, lookups); break; +#ifndef HB_NO_BEYOND_64K + case 2: find_lookups (graph, lookups); break; +#endif + } + } + + unsigned get_lookup_list_index (graph_t& graph) + { + return graph.index_for_offset (graph.root_idx (), + get_lookup_list_field_offset()); + } + + template + void find_lookups (graph_t& graph, + hb_hashmap_t& lookups /* OUT */) + { + unsigned lookup_list_idx = get_lookup_list_index (graph); + const LookupList* lookupList = + (const LookupList*) graph.object (lookup_list_idx).head; + if (!lookupList || !lookupList->sanitize (graph.vertices_[lookup_list_idx])) + return; + + for (unsigned i = 0; i < lookupList->len; i++) + { + unsigned lookup_idx = graph.index_for_offset (lookup_list_idx, &(lookupList->arrayZ[i])); + Lookup* lookup = (Lookup*) graph.object (lookup_idx).head; + if (!lookup || !lookup->sanitize (graph.vertices_[lookup_idx])) continue; + lookups.set (lookup_idx, lookup); + } + } +}; + + + + +} + +#endif /* GRAPH_GSUBGPOS_GRAPH_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/graph/markbasepos-graph.hh b/src/3rdparty/harfbuzz-ng/src/graph/markbasepos-graph.hh new file mode 100644 index 00000000000..ae5ebd0d167 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/graph/markbasepos-graph.hh @@ -0,0 +1,515 @@ +/* + * Copyright © 2022 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 + */ + +#ifndef GRAPH_MARKBASEPOS_GRAPH_HH +#define GRAPH_MARKBASEPOS_GRAPH_HH + +#include "split-helpers.hh" +#include "coverage-graph.hh" +#include "../OT/Layout/GPOS/MarkBasePos.hh" +#include "../OT/Layout/GPOS/PosLookupSubTable.hh" + +namespace graph { + +struct AnchorMatrix : public OT::Layout::GPOS_impl::AnchorMatrix +{ + bool sanitize (graph_t::vertex_t& vertex, unsigned class_count) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + if (vertex_len < AnchorMatrix::min_size) return false; + + return vertex_len >= AnchorMatrix::min_size + + OT::Offset16::static_size * class_count * this->rows; + } + + bool shrink (gsubgpos_graph_context_t& c, + unsigned this_index, + unsigned old_class_count, + unsigned new_class_count) + { + if (new_class_count >= old_class_count) return false; + auto& o = c.graph.vertices_[this_index].obj; + unsigned base_count = rows; + o.tail = o.head + + AnchorMatrix::min_size + + OT::Offset16::static_size * base_count * new_class_count; + + // Reposition links into the new indexing scheme. + for (auto& link : o.real_links.writer ()) + { + unsigned index = (link.position - 2) / 2; + unsigned base = index / old_class_count; + unsigned klass = index % old_class_count; + if (klass >= new_class_count) + // should have already been removed + return false; + + unsigned new_index = base * new_class_count + klass; + + link.position = (char*) &(this->matrixZ[new_index]) - (char*) this; + } + + return true; + } + + unsigned clone (gsubgpos_graph_context_t& c, + unsigned this_index, + unsigned start, + unsigned end, + unsigned class_count) + { + unsigned base_count = rows; + unsigned new_class_count = end - start; + unsigned size = AnchorMatrix::min_size + + OT::Offset16::static_size * new_class_count * rows; + unsigned prime_id = c.create_node (size); + if (prime_id == (unsigned) -1) return -1; + AnchorMatrix* prime = (AnchorMatrix*) c.graph.object (prime_id).head; + prime->rows = base_count; + + auto& o = c.graph.vertices_[this_index].obj; + int num_links = o.real_links.length; + for (int i = 0; i < num_links; i++) + { + const auto& link = o.real_links[i]; + unsigned old_index = (link.position - 2) / OT::Offset16::static_size; + unsigned klass = old_index % class_count; + if (klass < start || klass >= end) continue; + + unsigned base = old_index / class_count; + unsigned new_klass = klass - start; + unsigned new_index = base * new_class_count + new_klass; + + + unsigned child_idx = link.objidx; + c.graph.add_link (&(prime->matrixZ[new_index]), + prime_id, + child_idx); + + auto& child = c.graph.vertices_[child_idx]; + child.remove_parent (this_index); + + o.real_links.remove_unordered (i); + num_links--; + i--; + } + + return prime_id; + } +}; + +struct MarkArray : public OT::Layout::GPOS_impl::MarkArray +{ + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + unsigned min_size = MarkArray::min_size; + if (vertex_len < min_size) return false; + + return vertex_len >= get_size (); + } + + bool shrink (gsubgpos_graph_context_t& c, + const hb_hashmap_t& mark_array_links, + unsigned this_index, + unsigned new_class_count) + { + auto& o = c.graph.vertices_[this_index].obj; + for (const auto& link : o.real_links) + c.graph.vertices_[link.objidx].remove_parent (this_index); + o.real_links.reset (); + + unsigned new_index = 0; + for (const auto& record : this->iter ()) + { + unsigned klass = record.klass; + if (klass >= new_class_count) continue; + + (*this)[new_index].klass = klass; + unsigned position = (char*) &record.markAnchor - (char*) this; + unsigned* objidx; + if (!mark_array_links.has (position, &objidx)) + { + new_index++; + continue; + } + + c.graph.add_link (&(*this)[new_index].markAnchor, this_index, *objidx); + new_index++; + } + + this->len = new_index; + o.tail = o.head + MarkArray::min_size + + OT::Layout::GPOS_impl::MarkRecord::static_size * new_index; + return true; + } + + unsigned clone (gsubgpos_graph_context_t& c, + unsigned this_index, + const hb_hashmap_t& pos_to_index, + hb_set_t& marks, + unsigned start_class) + { + unsigned size = MarkArray::min_size + + OT::Layout::GPOS_impl::MarkRecord::static_size * + marks.get_population (); + unsigned prime_id = c.create_node (size); + if (prime_id == (unsigned) -1) return -1; + MarkArray* prime = (MarkArray*) c.graph.object (prime_id).head; + prime->len = marks.get_population (); + + + unsigned i = 0; + for (hb_codepoint_t mark : marks) + { + (*prime)[i].klass = (*this)[mark].klass - start_class; + unsigned offset_pos = (char*) &((*this)[mark].markAnchor) - (char*) this; + unsigned* anchor_index; + if (pos_to_index.has (offset_pos, &anchor_index)) + c.graph.move_child (this_index, + &((*this)[mark].markAnchor), + prime_id, + &((*prime)[i].markAnchor)); + + i++; + } + + return prime_id; + } +}; + +struct MarkBasePosFormat1 : public OT::Layout::GPOS_impl::MarkBasePosFormat1_2 +{ + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + return vertex_len >= MarkBasePosFormat1::static_size; + } + + hb_vector_t split_subtables (gsubgpos_graph_context_t& c, + unsigned parent_index, + unsigned this_index) + { + hb_set_t visited; + + const unsigned base_coverage_id = c.graph.index_for_offset (this_index, &baseCoverage); + const unsigned base_size = + OT::Layout::GPOS_impl::MarkBasePosFormat1_2::min_size + + MarkArray::min_size + + AnchorMatrix::min_size + + c.graph.vertices_[base_coverage_id].table_size (); + + hb_vector_t class_to_info = get_class_info (c, this_index); + + unsigned class_count = classCount; + auto base_array = c.graph.as_table (this_index, + &baseArray, + class_count); + if (!base_array) return hb_vector_t (); + unsigned base_count = base_array.table->rows; + + unsigned partial_coverage_size = 4; + unsigned accumulated = base_size; + hb_vector_t split_points; + + for (unsigned klass = 0; klass < class_count; klass++) + { + class_info_t& info = class_to_info[klass]; + partial_coverage_size += OT::HBUINT16::static_size * info.marks.get_population (); + unsigned accumulated_delta = + OT::Layout::GPOS_impl::MarkRecord::static_size * info.marks.get_population () + + OT::Offset16::static_size * base_count; + + for (unsigned objidx : info.child_indices) + accumulated_delta += c.graph.find_subgraph_size (objidx, visited); + + accumulated += accumulated_delta; + unsigned total = accumulated + partial_coverage_size; + + if (total >= (1 << 16)) + { + split_points.push (klass); + accumulated = base_size + accumulated_delta; + partial_coverage_size = 4 + OT::HBUINT16::static_size * info.marks.get_population (); + visited.clear (); // node sharing isn't allowed between splits. + } + } + + + const unsigned mark_array_id = c.graph.index_for_offset (this_index, &markArray); + split_context_t split_context { + c, + this, + c.graph.duplicate_if_shared (parent_index, this_index), + std::move (class_to_info), + c.graph.vertices_[mark_array_id].position_to_index_map (), + }; + + return actuate_subtable_split (split_context, split_points); + } + + private: + + struct class_info_t { + hb_set_t marks; + hb_vector_t child_indices; + }; + + struct split_context_t { + gsubgpos_graph_context_t& c; + MarkBasePosFormat1* thiz; + unsigned this_index; + hb_vector_t class_to_info; + hb_hashmap_t mark_array_links; + + hb_set_t marks_for (unsigned start, unsigned end) + { + hb_set_t marks; + for (unsigned klass = start; klass < end; klass++) + { + + class_to_info[klass].marks.iter () + | hb_sink (marks) + ; + } + return marks; + } + + unsigned original_count () + { + return thiz->classCount; + } + + unsigned clone_range (unsigned start, unsigned end) + { + return thiz->clone_range (*this, this->this_index, start, end); + } + + bool shrink (unsigned count) + { + return thiz->shrink (*this, this->this_index, count); + } + }; + + hb_vector_t get_class_info (gsubgpos_graph_context_t& c, + unsigned this_index) + { + hb_vector_t class_to_info; + + unsigned class_count = classCount; + if (!class_count) return class_to_info; + + if (!class_to_info.resize (class_count)) + return hb_vector_t(); + + auto mark_array = c.graph.as_table (this_index, &markArray); + if (!mark_array) return hb_vector_t (); + unsigned mark_count = mark_array.table->len; + for (unsigned mark = 0; mark < mark_count; mark++) + { + unsigned klass = (*mark_array.table)[mark].get_class (); + if (klass >= class_count) continue; + class_to_info[klass].marks.add (mark); + } + + for (const auto& link : mark_array.vertex->obj.real_links) + { + unsigned mark = (link.position - 2) / + OT::Layout::GPOS_impl::MarkRecord::static_size; + unsigned klass = (*mark_array.table)[mark].get_class (); + if (klass >= class_count) continue; + class_to_info[klass].child_indices.push (link.objidx); + } + + unsigned base_array_id = + c.graph.index_for_offset (this_index, &baseArray); + auto& base_array_v = c.graph.vertices_[base_array_id]; + + for (const auto& link : base_array_v.obj.real_links) + { + unsigned index = (link.position - 2) / OT::Offset16::static_size; + unsigned klass = index % class_count; + class_to_info[klass].child_indices.push (link.objidx); + } + + return class_to_info; + } + + bool shrink (split_context_t& sc, + unsigned this_index, + unsigned count) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + " Shrinking MarkBasePosFormat1 (%u) to [0, %u).", + this_index, + count); + + unsigned old_count = classCount; + if (count >= old_count) + return true; + + classCount = count; + + auto mark_coverage = sc.c.graph.as_mutable_table (this_index, + &markCoverage); + if (!mark_coverage) return false; + hb_set_t marks = sc.marks_for (0, count); + auto new_coverage = + + hb_enumerate (mark_coverage.table->iter ()) + | hb_filter (marks, hb_first) + | hb_map_retains_sorting (hb_second) + ; + if (!Coverage::make_coverage (sc.c, + new_coverage, + mark_coverage.index, + 4 + 2 * marks.get_population ())) + return false; + + + auto base_array = sc.c.graph.as_mutable_table (this_index, + &baseArray, + old_count); + if (!base_array || !base_array.table->shrink (sc.c, + base_array.index, + old_count, + count)) + return false; + + auto mark_array = sc.c.graph.as_mutable_table (this_index, + &markArray); + if (!mark_array || !mark_array.table->shrink (sc.c, + sc.mark_array_links, + mark_array.index, + count)) + return false; + + return true; + } + + // Create a new MarkBasePos that has all of the data for classes from [start, end). + unsigned clone_range (split_context_t& sc, + unsigned this_index, + unsigned start, unsigned end) const + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + " Cloning MarkBasePosFormat1 (%u) range [%u, %u).", this_index, start, end); + + graph_t& graph = sc.c.graph; + unsigned prime_size = OT::Layout::GPOS_impl::MarkBasePosFormat1_2::static_size; + + unsigned prime_id = sc.c.create_node (prime_size); + if (prime_id == (unsigned) -1) return -1; + + MarkBasePosFormat1* prime = (MarkBasePosFormat1*) graph.object (prime_id).head; + prime->format = this->format; + unsigned new_class_count = end - start; + prime->classCount = new_class_count; + + unsigned base_coverage_id = + graph.index_for_offset (sc.this_index, &baseCoverage); + graph.add_link (&(prime->baseCoverage), prime_id, base_coverage_id); + graph.duplicate (prime_id, base_coverage_id); + + auto mark_coverage = sc.c.graph.as_table (this_index, + &markCoverage); + if (!mark_coverage) return false; + hb_set_t marks = sc.marks_for (start, end); + auto new_coverage = + + hb_enumerate (mark_coverage.table->iter ()) + | hb_filter (marks, hb_first) + | hb_map_retains_sorting (hb_second) + ; + if (!Coverage::add_coverage (sc.c, + prime_id, + 2, + + new_coverage, + marks.get_population () * 2 + 4)) + return -1; + + auto mark_array = + graph.as_table (sc.this_index, &markArray); + if (!mark_array) return -1; + unsigned new_mark_array = + mark_array.table->clone (sc.c, + mark_array.index, + sc.mark_array_links, + marks, + start); + graph.add_link (&(prime->markArray), prime_id, new_mark_array); + + unsigned class_count = classCount; + auto base_array = + graph.as_table (sc.this_index, &baseArray, class_count); + if (!base_array) return -1; + unsigned new_base_array = + base_array.table->clone (sc.c, + base_array.index, + start, end, this->classCount); + graph.add_link (&(prime->baseArray), prime_id, new_base_array); + + return prime_id; + } +}; + + +struct MarkBasePos : public OT::Layout::GPOS_impl::MarkBasePos +{ + hb_vector_t split_subtables (gsubgpos_graph_context_t& c, + unsigned parent_index, + unsigned this_index) + { + switch (u.format) { + case 1: + return ((MarkBasePosFormat1*)(&u.format1))->split_subtables (c, parent_index, this_index); +#ifndef HB_NO_BEYOND_64K + case 2: HB_FALLTHROUGH; + // Don't split 24bit MarkBasePos's. +#endif + default: + return hb_vector_t (); + } + } + + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + if (vertex_len < u.format.get_size ()) return false; + + switch (u.format) { + case 1: + return ((MarkBasePosFormat1*)(&u.format1))->sanitize (vertex); +#ifndef HB_NO_BEYOND_64K + case 2: HB_FALLTHROUGH; +#endif + default: + // We don't handle format 3 and 4 here. + return false; + } + } +}; + + +} + +#endif // GRAPH_MARKBASEPOS_GRAPH_HH diff --git a/src/3rdparty/harfbuzz-ng/src/graph/pairpos-graph.hh b/src/3rdparty/harfbuzz-ng/src/graph/pairpos-graph.hh new file mode 100644 index 00000000000..ad158cc9e8f --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/graph/pairpos-graph.hh @@ -0,0 +1,647 @@ +/* + * Copyright © 2022 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 + */ + +#ifndef GRAPH_PAIRPOS_GRAPH_HH +#define GRAPH_PAIRPOS_GRAPH_HH + +#include "split-helpers.hh" +#include "coverage-graph.hh" +#include "classdef-graph.hh" +#include "../OT/Layout/GPOS/PairPos.hh" +#include "../OT/Layout/GPOS/PosLookupSubTable.hh" + +namespace graph { + +struct PairPosFormat1 : public OT::Layout::GPOS_impl::PairPosFormat1_3 +{ + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + unsigned min_size = OT::Layout::GPOS_impl::PairPosFormat1_3::min_size; + if (vertex_len < min_size) return false; + + return vertex_len >= + min_size + pairSet.get_size () - pairSet.len.get_size(); + } + + hb_vector_t split_subtables (gsubgpos_graph_context_t& c, + unsigned parent_index, + unsigned this_index) + { + hb_set_t visited; + + const unsigned coverage_id = c.graph.index_for_offset (this_index, &coverage); + const unsigned coverage_size = c.graph.vertices_[coverage_id].table_size (); + const unsigned base_size = OT::Layout::GPOS_impl::PairPosFormat1_3::min_size; + + unsigned partial_coverage_size = 4; + unsigned accumulated = base_size; + hb_vector_t split_points; + for (unsigned i = 0; i < pairSet.len; i++) + { + unsigned pair_set_index = pair_set_graph_index (c, this_index, i); + unsigned accumulated_delta = + c.graph.find_subgraph_size (pair_set_index, visited) + + SmallTypes::size; // for PairSet offset. + partial_coverage_size += OT::HBUINT16::static_size; + + accumulated += accumulated_delta; + unsigned total = accumulated + hb_min (partial_coverage_size, coverage_size); + + if (total >= (1 << 16)) + { + split_points.push (i); + accumulated = base_size + accumulated_delta; + partial_coverage_size = 6; + visited.clear (); // node sharing isn't allowed between splits. + } + } + + split_context_t split_context { + c, + this, + c.graph.duplicate_if_shared (parent_index, this_index), + }; + + return actuate_subtable_split (split_context, split_points); + } + + private: + + struct split_context_t { + gsubgpos_graph_context_t& c; + PairPosFormat1* thiz; + unsigned this_index; + + unsigned original_count () + { + return thiz->pairSet.len; + } + + unsigned clone_range (unsigned start, unsigned end) + { + return thiz->clone_range (this->c, this->this_index, start, end); + } + + bool shrink (unsigned count) + { + return thiz->shrink (this->c, this->this_index, count); + } + }; + + bool shrink (gsubgpos_graph_context_t& c, + unsigned this_index, + unsigned count) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + " Shrinking PairPosFormat1 (%u) to [0, %u).", + this_index, + count); + unsigned old_count = pairSet.len; + if (count >= old_count) + return true; + + pairSet.len = count; + c.graph.vertices_[this_index].obj.tail -= (old_count - count) * SmallTypes::size; + + auto coverage = c.graph.as_mutable_table (this_index, &this->coverage); + if (!coverage) return false; + + unsigned coverage_size = coverage.vertex->table_size (); + auto new_coverage = + + hb_zip (coverage.table->iter (), hb_range ()) + | hb_filter ([&] (hb_pair_t p) { + return p.second < count; + }) + | hb_map_retains_sorting (hb_first) + ; + + return Coverage::make_coverage (c, new_coverage, coverage.index, coverage_size); + } + + // Create a new PairPos including PairSet's from start (inclusive) to end (exclusive). + // Returns object id of the new object. + unsigned clone_range (gsubgpos_graph_context_t& c, + unsigned this_index, + unsigned start, unsigned end) const + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + " Cloning PairPosFormat1 (%u) range [%u, %u).", this_index, start, end); + + unsigned num_pair_sets = end - start; + unsigned prime_size = OT::Layout::GPOS_impl::PairPosFormat1_3::min_size + + num_pair_sets * SmallTypes::size; + + unsigned pair_pos_prime_id = c.create_node (prime_size); + if (pair_pos_prime_id == (unsigned) -1) return -1; + + PairPosFormat1* pair_pos_prime = (PairPosFormat1*) c.graph.object (pair_pos_prime_id).head; + pair_pos_prime->format = this->format; + pair_pos_prime->valueFormat[0] = this->valueFormat[0]; + pair_pos_prime->valueFormat[1] = this->valueFormat[1]; + pair_pos_prime->pairSet.len = num_pair_sets; + + for (unsigned i = start; i < end; i++) + { + c.graph.move_child<> (this_index, + &pairSet[i], + pair_pos_prime_id, + &pair_pos_prime->pairSet[i - start]); + } + + unsigned coverage_id = c.graph.index_for_offset (this_index, &coverage); + if (!Coverage::clone_coverage (c, + coverage_id, + pair_pos_prime_id, + 2, + start, end)) + return -1; + + return pair_pos_prime_id; + } + + + + unsigned pair_set_graph_index (gsubgpos_graph_context_t& c, unsigned this_index, unsigned i) const + { + return c.graph.index_for_offset (this_index, &pairSet[i]); + } +}; + +struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4 +{ + bool sanitize (graph_t::vertex_t& vertex) const + { + size_t vertex_len = vertex.table_size (); + unsigned min_size = OT::Layout::GPOS_impl::PairPosFormat2_4::min_size; + if (vertex_len < min_size) return false; + + const unsigned class1_count = class1Count; + return vertex_len >= + min_size + class1_count * get_class1_record_size (); + } + + hb_vector_t split_subtables (gsubgpos_graph_context_t& c, + unsigned parent_index, + unsigned this_index) + { + const unsigned base_size = OT::Layout::GPOS_impl::PairPosFormat2_4::min_size; + const unsigned class_def_2_size = size_of (c, this_index, &classDef2); + const Coverage* coverage = get_coverage (c, this_index); + const ClassDef* class_def_1 = get_class_def_1 (c, this_index); + auto gid_and_class = + + coverage->iter () + | hb_map_retains_sorting ([&] (hb_codepoint_t gid) { + return hb_codepoint_pair_t (gid, class_def_1->get_class (gid)); + }) + ; + class_def_size_estimator_t estimator (gid_and_class); + + const unsigned class1_count = class1Count; + const unsigned class2_count = class2Count; + const unsigned class1_record_size = get_class1_record_size (); + + const unsigned value_1_len = valueFormat1.get_len (); + const unsigned value_2_len = valueFormat2.get_len (); + const unsigned total_value_len = value_1_len + value_2_len; + + unsigned accumulated = base_size; + unsigned coverage_size = 4; + unsigned class_def_1_size = 4; + unsigned max_coverage_size = coverage_size; + unsigned max_class_def_1_size = class_def_1_size; + + hb_vector_t split_points; + + hb_hashmap_t device_tables = get_all_device_tables (c, this_index); + hb_vector_t format1_device_table_indices = valueFormat1.get_device_table_indices (); + hb_vector_t format2_device_table_indices = valueFormat2.get_device_table_indices (); + bool has_device_tables = bool(format1_device_table_indices) || bool(format2_device_table_indices); + + hb_set_t visited; + for (unsigned i = 0; i < class1_count; i++) + { + unsigned accumulated_delta = class1_record_size; + coverage_size += estimator.incremental_coverage_size (i); + class_def_1_size += estimator.incremental_class_def_size (i); + max_coverage_size = hb_max (max_coverage_size, coverage_size); + max_class_def_1_size = hb_max (max_class_def_1_size, class_def_1_size); + + if (has_device_tables) { + for (unsigned j = 0; j < class2_count; j++) + { + unsigned value1_index = total_value_len * (class2_count * i + j); + unsigned value2_index = value1_index + value_1_len; + accumulated_delta += size_of_value_record_children (c, + device_tables, + format1_device_table_indices, + value1_index, + visited); + accumulated_delta += size_of_value_record_children (c, + device_tables, + format2_device_table_indices, + value2_index, + visited); + } + } + + accumulated += accumulated_delta; + unsigned total = accumulated + + coverage_size + class_def_1_size + class_def_2_size + // The largest object will pack last and can exceed the size limit. + - hb_max (hb_max (coverage_size, class_def_1_size), class_def_2_size); + if (total >= (1 << 16)) + { + split_points.push (i); + // split does not include i, so add the size for i when we reset the size counters. + accumulated = base_size + accumulated_delta; + coverage_size = 4 + estimator.incremental_coverage_size (i); + class_def_1_size = 4 + estimator.incremental_class_def_size (i); + visited.clear (); // node sharing isn't allowed between splits. + } + } + + split_context_t split_context { + c, + this, + c.graph.duplicate_if_shared (parent_index, this_index), + class1_record_size, + total_value_len, + value_1_len, + value_2_len, + max_coverage_size, + max_class_def_1_size, + device_tables, + format1_device_table_indices, + format2_device_table_indices + }; + + return actuate_subtable_split (split_context, split_points); + } + private: + + struct split_context_t + { + gsubgpos_graph_context_t& c; + PairPosFormat2* thiz; + unsigned this_index; + unsigned class1_record_size; + unsigned value_record_len; + unsigned value1_record_len; + unsigned value2_record_len; + unsigned max_coverage_size; + unsigned max_class_def_size; + + const hb_hashmap_t& device_tables; + const hb_vector_t& format1_device_table_indices; + const hb_vector_t& format2_device_table_indices; + + unsigned original_count () + { + return thiz->class1Count; + } + + unsigned clone_range (unsigned start, unsigned end) + { + return thiz->clone_range (*this, start, end); + } + + bool shrink (unsigned count) + { + return thiz->shrink (*this, count); + } + }; + + size_t get_class1_record_size () const + { + const size_t class2_count = class2Count; + return + class2_count * (valueFormat1.get_size () + valueFormat2.get_size ()); + } + + unsigned clone_range (split_context_t& split_context, + unsigned start, unsigned end) const + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + " Cloning PairPosFormat2 (%u) range [%u, %u).", split_context.this_index, start, end); + + graph_t& graph = split_context.c.graph; + + unsigned num_records = end - start; + unsigned prime_size = OT::Layout::GPOS_impl::PairPosFormat2_4::min_size + + num_records * split_context.class1_record_size; + + unsigned pair_pos_prime_id = split_context.c.create_node (prime_size); + if (pair_pos_prime_id == (unsigned) -1) return -1; + + PairPosFormat2* pair_pos_prime = + (PairPosFormat2*) graph.object (pair_pos_prime_id).head; + pair_pos_prime->format = this->format; + pair_pos_prime->valueFormat1 = this->valueFormat1; + pair_pos_prime->valueFormat2 = this->valueFormat2; + pair_pos_prime->class1Count = num_records; + pair_pos_prime->class2Count = this->class2Count; + clone_class1_records (split_context, + pair_pos_prime_id, + start, + end); + + unsigned coverage_id = + graph.index_for_offset (split_context.this_index, &coverage); + unsigned class_def_1_id = + graph.index_for_offset (split_context.this_index, &classDef1); + auto& coverage_v = graph.vertices_[coverage_id]; + auto& class_def_1_v = graph.vertices_[class_def_1_id]; + Coverage* coverage_table = (Coverage*) coverage_v.obj.head; + ClassDef* class_def_1_table = (ClassDef*) class_def_1_v.obj.head; + if (!coverage_table + || !coverage_table->sanitize (coverage_v) + || !class_def_1_table + || !class_def_1_table->sanitize (class_def_1_v)) + return -1; + + auto klass_map = + + coverage_table->iter () + | hb_map_retains_sorting ([&] (hb_codepoint_t gid) { + return hb_codepoint_pair_t (gid, class_def_1_table->get_class (gid)); + }) + | hb_filter ([&] (hb_codepoint_t klass) { + return klass >= start && klass < end; + }, hb_second) + | hb_map_retains_sorting ([&] (hb_codepoint_pair_t gid_and_class) { + // Classes must be from 0...N so subtract start + return hb_codepoint_pair_t (gid_and_class.first, gid_and_class.second - start); + }) + ; + + if (!Coverage::add_coverage (split_context.c, + pair_pos_prime_id, + 2, + + klass_map | hb_map_retains_sorting (hb_first), + split_context.max_coverage_size)) + return -1; + + // classDef1 + if (!ClassDef::add_class_def (split_context.c, + pair_pos_prime_id, + 8, + + klass_map, + split_context.max_class_def_size)) + return -1; + + // classDef2 + unsigned class_def_2_id = + graph.index_for_offset (split_context.this_index, &classDef2); + auto* class_def_link = graph.vertices_[pair_pos_prime_id].obj.real_links.push (); + class_def_link->width = SmallTypes::size; + class_def_link->objidx = class_def_2_id; + class_def_link->position = 10; + graph.vertices_[class_def_2_id].add_parent (pair_pos_prime_id); + graph.duplicate (pair_pos_prime_id, class_def_2_id); + + return pair_pos_prime_id; + } + + void clone_class1_records (split_context_t& split_context, + unsigned pair_pos_prime_id, + unsigned start, unsigned end) const + { + PairPosFormat2* pair_pos_prime = + (PairPosFormat2*) split_context.c.graph.object (pair_pos_prime_id).head; + + char* start_addr = ((char*)&values[0]) + start * split_context.class1_record_size; + unsigned num_records = end - start; + hb_memcpy (&pair_pos_prime->values[0], + start_addr, + num_records * split_context.class1_record_size); + + if (!split_context.format1_device_table_indices + && !split_context.format2_device_table_indices) + // No device tables to move over. + return; + + unsigned class2_count = class2Count; + for (unsigned i = start; i < end; i++) + { + for (unsigned j = 0; j < class2_count; j++) + { + unsigned value1_index = split_context.value_record_len * (class2_count * i + j); + unsigned value2_index = value1_index + split_context.value1_record_len; + + unsigned new_value1_index = split_context.value_record_len * (class2_count * (i - start) + j); + unsigned new_value2_index = new_value1_index + split_context.value1_record_len; + + transfer_device_tables (split_context, + pair_pos_prime_id, + split_context.format1_device_table_indices, + value1_index, + new_value1_index); + + transfer_device_tables (split_context, + pair_pos_prime_id, + split_context.format2_device_table_indices, + value2_index, + new_value2_index); + } + } + } + + void transfer_device_tables (split_context_t& split_context, + unsigned pair_pos_prime_id, + const hb_vector_t& device_table_indices, + unsigned old_value_record_index, + unsigned new_value_record_index) const + { + PairPosFormat2* pair_pos_prime = + (PairPosFormat2*) split_context.c.graph.object (pair_pos_prime_id).head; + + for (unsigned i : device_table_indices) + { + OT::Offset16* record = (OT::Offset16*) &values[old_value_record_index + i]; + unsigned record_position = ((char*) record) - ((char*) this); + if (!split_context.device_tables.has (record_position)) continue; + + split_context.c.graph.move_child ( + split_context.this_index, + record, + pair_pos_prime_id, + (OT::Offset16*) &pair_pos_prime->values[new_value_record_index + i]); + } + } + + bool shrink (split_context_t& split_context, + unsigned count) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + " Shrinking PairPosFormat2 (%u) to [0, %u).", + split_context.this_index, + count); + unsigned old_count = class1Count; + if (count >= old_count) + return true; + + graph_t& graph = split_context.c.graph; + class1Count = count; + graph.vertices_[split_context.this_index].obj.tail -= + (old_count - count) * split_context.class1_record_size; + + auto coverage = + graph.as_mutable_table (split_context.this_index, &this->coverage); + if (!coverage) return false; + + auto class_def_1 = + graph.as_mutable_table (split_context.this_index, &classDef1); + if (!class_def_1) return false; + + auto klass_map = + + coverage.table->iter () + | hb_map_retains_sorting ([&] (hb_codepoint_t gid) { + return hb_codepoint_pair_t (gid, class_def_1.table->get_class (gid)); + }) + | hb_filter ([&] (hb_codepoint_t klass) { + return klass < count; + }, hb_second) + ; + + auto new_coverage = + klass_map | hb_map_retains_sorting (hb_first); + if (!Coverage::make_coverage (split_context.c, + + new_coverage, + coverage.index, + // existing ranges my not be kept, worst case size is a format 1 + // coverage table. + 4 + new_coverage.len() * 2)) + return false; + + return ClassDef::make_class_def (split_context.c, + + klass_map, + class_def_1.index, + class_def_1.vertex->table_size ()); + } + + hb_hashmap_t + get_all_device_tables (gsubgpos_graph_context_t& c, + unsigned this_index) const + { + const auto& v = c.graph.vertices_[this_index]; + return v.position_to_index_map (); + } + + const Coverage* get_coverage (gsubgpos_graph_context_t& c, + unsigned this_index) const + { + unsigned coverage_id = c.graph.index_for_offset (this_index, &coverage); + auto& coverage_v = c.graph.vertices_[coverage_id]; + + Coverage* coverage_table = (Coverage*) coverage_v.obj.head; + if (!coverage_table || !coverage_table->sanitize (coverage_v)) + return &Null(Coverage); + return coverage_table; + } + + const ClassDef* get_class_def_1 (gsubgpos_graph_context_t& c, + unsigned this_index) const + { + unsigned class_def_1_id = c.graph.index_for_offset (this_index, &classDef1); + auto& class_def_1_v = c.graph.vertices_[class_def_1_id]; + + ClassDef* class_def_1_table = (ClassDef*) class_def_1_v.obj.head; + if (!class_def_1_table || !class_def_1_table->sanitize (class_def_1_v)) + return &Null(ClassDef); + return class_def_1_table; + } + + unsigned size_of_value_record_children (gsubgpos_graph_context_t& c, + const hb_hashmap_t& device_tables, + const hb_vector_t device_table_indices, + unsigned value_record_index, + hb_set_t& visited) + { + unsigned size = 0; + for (unsigned i : device_table_indices) + { + OT::Layout::GPOS_impl::Value* record = &values[value_record_index + i]; + unsigned record_position = ((char*) record) - ((char*) this); + unsigned* obj_idx; + if (!device_tables.has (record_position, &obj_idx)) continue; + size += c.graph.find_subgraph_size (*obj_idx, visited); + } + return size; + } + + unsigned size_of (gsubgpos_graph_context_t& c, + unsigned this_index, + const void* offset) const + { + const unsigned id = c.graph.index_for_offset (this_index, offset); + return c.graph.vertices_[id].table_size (); + } +}; + +struct PairPos : public OT::Layout::GPOS_impl::PairPos +{ + hb_vector_t split_subtables (gsubgpos_graph_context_t& c, + unsigned parent_index, + unsigned this_index) + { + switch (u.format) { + case 1: + return ((PairPosFormat1*)(&u.format1))->split_subtables (c, parent_index, this_index); + case 2: + return ((PairPosFormat2*)(&u.format2))->split_subtables (c, parent_index, this_index); +#ifndef HB_NO_BEYOND_64K + case 3: HB_FALLTHROUGH; + case 4: HB_FALLTHROUGH; + // Don't split 24bit PairPos's. +#endif + default: + return hb_vector_t (); + } + } + + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + if (vertex_len < u.format.get_size ()) return false; + + switch (u.format) { + case 1: + return ((PairPosFormat1*)(&u.format1))->sanitize (vertex); + case 2: + return ((PairPosFormat2*)(&u.format2))->sanitize (vertex); +#ifndef HB_NO_BEYOND_64K + case 3: HB_FALLTHROUGH; + case 4: HB_FALLTHROUGH; +#endif + default: + // We don't handle format 3 and 4 here. + return false; + } + } +}; + +} + +#endif // GRAPH_PAIRPOS_GRAPH_HH diff --git a/src/3rdparty/harfbuzz-ng/src/graph/serialize.hh b/src/3rdparty/harfbuzz-ng/src/graph/serialize.hh new file mode 100644 index 00000000000..06e4bf44d8e --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/graph/serialize.hh @@ -0,0 +1,273 @@ +/* + * Copyright © 2022 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 + */ + +#ifndef GRAPH_SERIALIZE_HH +#define GRAPH_SERIALIZE_HH + +namespace graph { + +struct overflow_record_t +{ + unsigned parent; + unsigned child; + + bool operator != (const overflow_record_t o) const + { return !(*this == o); } + + inline bool operator == (const overflow_record_t& o) const + { + return parent == o.parent && + child == o.child; + } + + inline uint32_t hash () const + { + uint32_t current = 0; + current = current * 31 + hb_hash (parent); + current = current * 31 + hb_hash (child); + return current; + } +}; + +inline +int64_t compute_offset ( + const graph_t& graph, + unsigned parent_idx, + const hb_serialize_context_t::object_t::link_t& link) +{ + const auto& parent = graph.vertices_[parent_idx]; + const auto& child = graph.vertices_[link.objidx]; + int64_t offset = 0; + switch ((hb_serialize_context_t::whence_t) link.whence) { + case hb_serialize_context_t::whence_t::Head: + offset = child.start - parent.start; break; + case hb_serialize_context_t::whence_t::Tail: + offset = child.start - parent.end; break; + case hb_serialize_context_t::whence_t::Absolute: + offset = child.start; break; + } + + assert (offset >= link.bias); + offset -= link.bias; + return offset; +} + +inline +bool is_valid_offset (int64_t offset, + const hb_serialize_context_t::object_t::link_t& link) +{ + if (unlikely (!link.width)) + // Virtual links can't overflow. + return link.is_signed || offset >= 0; + + if (link.is_signed) + { + if (link.width == 4) + return offset >= -((int64_t) 1 << 31) && offset < ((int64_t) 1 << 31); + else + return offset >= -(1 << 15) && offset < (1 << 15); + } + else + { + if (link.width == 4) + return offset >= 0 && offset < ((int64_t) 1 << 32); + else if (link.width == 3) + return offset >= 0 && offset < ((int32_t) 1 << 24); + else + return offset >= 0 && offset < (1 << 16); + } +} + +/* + * Will any offsets overflow on graph when it's serialized? + */ +inline bool +will_overflow (graph_t& graph, + hb_vector_t* overflows = nullptr) +{ + if (overflows) overflows->resize (0); + graph.update_positions (); + + hb_hashmap_t record_set; + const auto& vertices = graph.vertices_; + for (int parent_idx = vertices.length - 1; parent_idx >= 0; parent_idx--) + { + // Don't need to check virtual links for overflow + for (const auto& link : vertices.arrayZ[parent_idx].obj.real_links) + { + int64_t offset = compute_offset (graph, parent_idx, link); + if (likely (is_valid_offset (offset, link))) + continue; + + if (!overflows) return true; + + overflow_record_t r; + r.parent = parent_idx; + r.child = link.objidx; + if (record_set.has(&r)) continue; // don't keep duplicate overflows. + + overflows->push (r); + record_set.set(&r, true); + } + } + + if (!overflows) return false; + return overflows->length; +} + +inline +void print_overflows (graph_t& graph, + const hb_vector_t& overflows) +{ + if (!DEBUG_ENABLED(SUBSET_REPACK)) return; + + graph.update_parents (); + int limit = 10; + for (const auto& o : overflows) + { + if (!limit--) break; + const auto& parent = graph.vertices_[o.parent]; + const auto& child = graph.vertices_[o.child]; + DEBUG_MSG (SUBSET_REPACK, nullptr, + " overflow from " + "%4u (%4u in, %4u out, space %2u) => " + "%4u (%4u in, %4u out, space %2u)", + o.parent, + parent.incoming_edges (), + parent.obj.real_links.length + parent.obj.virtual_links.length, + graph.space_for (o.parent), + o.child, + child.incoming_edges (), + child.obj.real_links.length + child.obj.virtual_links.length, + graph.space_for (o.child)); + } + if (overflows.length > 10) { + DEBUG_MSG (SUBSET_REPACK, nullptr, " ... plus %u more overflows.", overflows.length - 10); + } +} + +template inline void +serialize_link_of_type (const hb_serialize_context_t::object_t::link_t& link, + char* head, + hb_serialize_context_t* c) +{ + OT::Offset* offset = reinterpret_cast*> (head + link.position); + *offset = 0; + c->add_link (*offset, + // serializer has an extra nil object at the start of the + // object array. So all id's are +1 of what our id's are. + link.objidx + 1, + (hb_serialize_context_t::whence_t) link.whence, + link.bias); +} + +inline +void serialize_link (const hb_serialize_context_t::object_t::link_t& link, + char* head, + hb_serialize_context_t* c) +{ + switch (link.width) + { + case 0: + // Virtual links aren't serialized. + return; + case 4: + if (link.is_signed) + { + serialize_link_of_type (link, head, c); + } else { + serialize_link_of_type (link, head, c); + } + return; + case 2: + if (link.is_signed) + { + serialize_link_of_type (link, head, c); + } else { + serialize_link_of_type (link, head, c); + } + return; + case 3: + serialize_link_of_type (link, head, c); + return; + default: + // Unexpected link width. + assert (0); + } +} + +/* + * serialize graph into the provided serialization buffer. + */ +inline hb_blob_t* serialize (const graph_t& graph) +{ + hb_vector_t buffer; + size_t size = graph.total_size_in_bytes (); + + if (!size) return hb_blob_get_empty (); + + if (!buffer.alloc (size)) { + DEBUG_MSG (SUBSET_REPACK, nullptr, "Unable to allocate output buffer."); + return nullptr; + } + hb_serialize_context_t c((void *) buffer, size); + + c.start_serialize (); + const auto& vertices = graph.vertices_; + for (unsigned i = 0; i < vertices.length; i++) { + c.push (); + + size_t size = vertices[i].obj.tail - vertices[i].obj.head; + char* start = c.allocate_size (size); + if (!start) { + DEBUG_MSG (SUBSET_REPACK, nullptr, "Buffer out of space."); + return nullptr; + } + + hb_memcpy (start, vertices[i].obj.head, size); + + // Only real links needs to be serialized. + for (const auto& link : vertices[i].obj.real_links) + serialize_link (link, start, &c); + + // All duplications are already encoded in the graph, so don't + // enable sharing during packing. + c.pop_pack (false); + } + c.end_serialize (); + + if (c.in_error ()) { + DEBUG_MSG (SUBSET_REPACK, nullptr, "Error during serialization. Err flag: %d", + c.errors); + return nullptr; + } + + return c.copy_blob (); +} + +} // namespace graph + +#endif // GRAPH_SERIALIZE_HH diff --git a/src/3rdparty/harfbuzz-ng/src/graph/split-helpers.hh b/src/3rdparty/harfbuzz-ng/src/graph/split-helpers.hh new file mode 100644 index 00000000000..61fd7c2d2ff --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/graph/split-helpers.hh @@ -0,0 +1,69 @@ +/* + * Copyright © 2022 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 + */ + +#ifndef GRAPH_SPLIT_HELPERS_HH +#define GRAPH_SPLIT_HELPERS_HH + +namespace graph { + +template +HB_INTERNAL +hb_vector_t actuate_subtable_split (Context& split_context, + const hb_vector_t& split_points) +{ + hb_vector_t new_objects; + if (!split_points) + return new_objects; + + for (unsigned i = 0; i < split_points.length; i++) + { + unsigned start = split_points[i]; + unsigned end = (i < split_points.length - 1) + ? split_points[i + 1] + : split_context.original_count (); + unsigned id = split_context.clone_range (start, end); + + if (id == (unsigned) -1) + { + new_objects.reset (); + new_objects.allocated = -1; // mark error + return new_objects; + } + new_objects.push (id); + } + + if (!split_context.shrink (split_points[0])) + { + new_objects.reset (); + new_objects.allocated = -1; // mark error + } + + return new_objects; +} + +} + +#endif // GRAPH_SPLIT_HELPERS_HH diff --git a/src/3rdparty/harfbuzz-ng/src/graph/test-classdef-graph.cc b/src/3rdparty/harfbuzz-ng/src/graph/test-classdef-graph.cc new file mode 100644 index 00000000000..266be5e2d40 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/graph/test-classdef-graph.cc @@ -0,0 +1,119 @@ +/* + * Copyright © 2022 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 "gsubgpos-context.hh" +#include "classdef-graph.hh" + +typedef hb_codepoint_pair_t gid_and_class_t; +typedef hb_vector_t gid_and_class_list_t; + + +static bool incremental_size_is (const gid_and_class_list_t& list, unsigned klass, + unsigned cov_expected, unsigned class_def_expected) +{ + graph::class_def_size_estimator_t estimator (list.iter ()); + + unsigned result = estimator.incremental_coverage_size (klass); + if (result != cov_expected) + { + printf ("FAIL: coverage expected size %u but was %u\n", cov_expected, result); + return false; + } + + result = estimator.incremental_class_def_size (klass); + if (result != class_def_expected) + { + printf ("FAIL: class def expected size %u but was %u\n", class_def_expected, result); + return false; + } + + return true; +} + +static void test_class_and_coverage_size_estimates () +{ + gid_and_class_list_t empty = { + }; + assert (incremental_size_is (empty, 0, 0, 0)); + assert (incremental_size_is (empty, 1, 0, 0)); + + gid_and_class_list_t class_zero = { + {5, 0}, + }; + assert (incremental_size_is (class_zero, 0, 2, 0)); + + gid_and_class_list_t consecutive = { + {4, 0}, + {5, 0}, + {6, 1}, + {7, 1}, + {8, 2}, + {9, 2}, + {10, 2}, + {11, 2}, + }; + assert (incremental_size_is (consecutive, 0, 4, 0)); + assert (incremental_size_is (consecutive, 1, 4, 4)); + assert (incremental_size_is (consecutive, 2, 8, 6)); + + gid_and_class_list_t non_consecutive = { + {4, 0}, + {5, 0}, + + {6, 1}, + {7, 1}, + + {9, 2}, + {10, 2}, + {11, 2}, + {12, 2}, + }; + assert (incremental_size_is (non_consecutive, 0, 4, 0)); + assert (incremental_size_is (non_consecutive, 1, 4, 6)); + assert (incremental_size_is (non_consecutive, 2, 8, 6)); + + gid_and_class_list_t multiple_ranges = { + {4, 0}, + {5, 0}, + + {6, 1}, + {7, 1}, + + {9, 1}, + + {11, 1}, + {12, 1}, + {13, 1}, + }; + assert (incremental_size_is (multiple_ranges, 0, 4, 0)); + assert (incremental_size_is (multiple_ranges, 1, 2 * 6, 3 * 6)); +} + +int +main (int argc, char **argv) +{ + test_class_and_coverage_size_estimates (); +} diff --git a/src/3rdparty/harfbuzz-ng/src/harfbuzz-subset.cc b/src/3rdparty/harfbuzz-ng/src/harfbuzz-subset.cc new file mode 100644 index 00000000000..c0e23b3eb82 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/harfbuzz-subset.cc @@ -0,0 +1,62 @@ +#include "graph/gsubgpos-context.cc" +#include "hb-aat-layout.cc" +#include "hb-aat-map.cc" +#include "hb-blob.cc" +#include "hb-buffer-serialize.cc" +#include "hb-buffer-verify.cc" +#include "hb-buffer.cc" +#include "hb-common.cc" +#include "hb-draw.cc" +#include "hb-face-builder.cc" +#include "hb-face.cc" +#include "hb-fallback-shape.cc" +#include "hb-font.cc" +#include "hb-map.cc" +#include "hb-number.cc" +#include "hb-ot-cff1-table.cc" +#include "hb-ot-cff2-table.cc" +#include "hb-ot-color.cc" +#include "hb-ot-face.cc" +#include "hb-ot-font.cc" +#include "hb-ot-layout.cc" +#include "hb-ot-map.cc" +#include "hb-ot-math.cc" +#include "hb-ot-meta.cc" +#include "hb-ot-metrics.cc" +#include "hb-ot-name.cc" +#include "hb-ot-shape-fallback.cc" +#include "hb-ot-shape-normalize.cc" +#include "hb-ot-shape.cc" +#include "hb-ot-shaper-arabic.cc" +#include "hb-ot-shaper-default.cc" +#include "hb-ot-shaper-hangul.cc" +#include "hb-ot-shaper-hebrew.cc" +#include "hb-ot-shaper-indic-table.cc" +#include "hb-ot-shaper-indic.cc" +#include "hb-ot-shaper-khmer.cc" +#include "hb-ot-shaper-myanmar.cc" +#include "hb-ot-shaper-syllabic.cc" +#include "hb-ot-shaper-thai.cc" +#include "hb-ot-shaper-use.cc" +#include "hb-ot-shaper-vowel-constraints.cc" +#include "hb-ot-tag.cc" +#include "hb-ot-var.cc" +#include "hb-outline.cc" +#include "hb-paint-extents.cc" +#include "hb-paint.cc" +#include "hb-set.cc" +#include "hb-shape-plan.cc" +#include "hb-shape.cc" +#include "hb-shaper.cc" +#include "hb-static.cc" +#include "hb-style.cc" +#include "hb-subset-cff-common.cc" +#include "hb-subset-cff1.cc" +#include "hb-subset-cff2.cc" +#include "hb-subset-input.cc" +#include "hb-subset-instancer-solver.cc" +#include "hb-subset-plan.cc" +#include "hb-subset-repacker.cc" +#include "hb-subset.cc" +#include "hb-ucd.cc" +#include "hb-unicode.cc" diff --git a/src/3rdparty/harfbuzz-ng/src/harfbuzz.cc b/src/3rdparty/harfbuzz-ng/src/harfbuzz.cc new file mode 100644 index 00000000000..26e2bc1450d --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/harfbuzz.cc @@ -0,0 +1,62 @@ +#include "hb-aat-layout.cc" +#include "hb-aat-map.cc" +#include "hb-blob.cc" +#include "hb-buffer-serialize.cc" +#include "hb-buffer-verify.cc" +#include "hb-buffer.cc" +#include "hb-common.cc" +#include "hb-coretext.cc" +#include "hb-directwrite.cc" +#include "hb-draw.cc" +#include "hb-face-builder.cc" +#include "hb-face.cc" +#include "hb-fallback-shape.cc" +#include "hb-font.cc" +#include "hb-ft.cc" +#include "hb-gdi.cc" +#include "hb-glib.cc" +#include "hb-graphite2.cc" +#include "hb-map.cc" +#include "hb-number.cc" +#include "hb-ot-cff1-table.cc" +#include "hb-ot-cff2-table.cc" +#include "hb-ot-color.cc" +#include "hb-ot-face.cc" +#include "hb-ot-font.cc" +#include "hb-ot-layout.cc" +#include "hb-ot-map.cc" +#include "hb-ot-math.cc" +#include "hb-ot-meta.cc" +#include "hb-ot-metrics.cc" +#include "hb-ot-name.cc" +#include "hb-ot-shape-fallback.cc" +#include "hb-ot-shape-normalize.cc" +#include "hb-ot-shape.cc" +#include "hb-ot-shaper-arabic.cc" +#include "hb-ot-shaper-default.cc" +#include "hb-ot-shaper-hangul.cc" +#include "hb-ot-shaper-hebrew.cc" +#include "hb-ot-shaper-indic-table.cc" +#include "hb-ot-shaper-indic.cc" +#include "hb-ot-shaper-khmer.cc" +#include "hb-ot-shaper-myanmar.cc" +#include "hb-ot-shaper-syllabic.cc" +#include "hb-ot-shaper-thai.cc" +#include "hb-ot-shaper-use.cc" +#include "hb-ot-shaper-vowel-constraints.cc" +#include "hb-ot-tag.cc" +#include "hb-ot-var.cc" +#include "hb-outline.cc" +#include "hb-paint-extents.cc" +#include "hb-paint.cc" +#include "hb-set.cc" +#include "hb-shape-plan.cc" +#include "hb-shape.cc" +#include "hb-shaper.cc" +#include "hb-static.cc" +#include "hb-style.cc" +#include "hb-ucd.cc" +#include "hb-unicode.cc" +#include "hb-uniscribe.cc" +#include "hb-wasm-api.cc" +#include "hb-wasm-shape.cc" diff --git a/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-ankr-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-ankr-table.hh new file mode 100644 index 00000000000..63fac84524f --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-ankr-table.hh @@ -0,0 +1,98 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * + * 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. + */ + +#ifndef HB_AAT_LAYOUT_ANKR_TABLE_HH +#define HB_AAT_LAYOUT_ANKR_TABLE_HH + +#include "hb-aat-layout-common.hh" + +/* + * ankr -- Anchor Point + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6ankr.html + */ +#define HB_AAT_TAG_ankr HB_TAG('a','n','k','r') + + +namespace AAT { + +using namespace OT; + + +struct Anchor +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + FWORD xCoordinate; + FWORD yCoordinate; + public: + DEFINE_SIZE_STATIC (4); +}; + +typedef Array32Of GlyphAnchors; + +struct ankr +{ + static constexpr hb_tag_t tableTag = HB_AAT_TAG_ankr; + + const Anchor &get_anchor (hb_codepoint_t glyph_id, + unsigned int i, + unsigned int num_glyphs) const + { + const NNOffset16To *offset = (this+lookupTable).get_value (glyph_id, num_glyphs); + if (!offset) + return Null (Anchor); + const GlyphAnchors &anchors = &(this+anchorData) + *offset; + return anchors[i]; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + version == 0 && + c->check_range (this, anchorData) && + lookupTable.sanitize (c, this, &(this+anchorData)))); + } + + protected: + HBUINT16 version; /* Version number (set to zero) */ + HBUINT16 flags; /* Flags (currently unused; set to zero) */ + Offset32To>> + lookupTable; /* Offset to the table's lookup table */ + NNOffset32To + anchorData; /* Offset to the glyph data table */ + + public: + DEFINE_SIZE_STATIC (12); +}; + +} /* namespace AAT */ + + +#endif /* HB_AAT_LAYOUT_ANKR_TABLE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-bsln-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-bsln-table.hh new file mode 100644 index 00000000000..bf12d2e699f --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-bsln-table.hh @@ -0,0 +1,158 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * + * 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. + */ + +#ifndef HB_AAT_LAYOUT_BSLN_TABLE_HH +#define HB_AAT_LAYOUT_BSLN_TABLE_HH + +#include "hb-aat-layout-common.hh" + +/* + * bsln -- Baseline + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6bsln.html + */ +#define HB_AAT_TAG_bsln HB_TAG('b','s','l','n') + + +namespace AAT { + + +struct BaselineTableFormat0Part +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + // Roman, Ideographic centered, Ideographic low, Hanging and Math + // are the default defined ones, but any other maybe accessed also. + HBINT16 deltas[32]; /* These are the FUnit distance deltas from + * the font's natural baseline to the other + * baselines used in the font. */ + public: + DEFINE_SIZE_STATIC (64); +}; + +struct BaselineTableFormat1Part +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + lookupTable.sanitize (c))); + } + + protected: + HBINT16 deltas[32]; /* ditto */ + Lookup + lookupTable; /* Lookup table that maps glyphs to their + * baseline values. */ + public: + DEFINE_SIZE_MIN (66); +}; + +struct BaselineTableFormat2Part +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + HBGlyphID16 stdGlyph; /* The specific glyph index number in this + * font that is used to set the baseline values. + * This is the standard glyph. + * This glyph must contain a set of control points + * (whose numbers are contained in the ctlPoints field) + * that are used to determine baseline distances. */ + HBUINT16 ctlPoints[32]; /* Set of control point numbers, + * associated with the standard glyph. + * A value of 0xFFFF means there is no corresponding + * control point in the standard glyph. */ + public: + DEFINE_SIZE_STATIC (66); +}; + +struct BaselineTableFormat3Part +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && lookupTable.sanitize (c))); + } + + protected: + HBGlyphID16 stdGlyph; /* ditto */ + HBUINT16 ctlPoints[32]; /* ditto */ + Lookup + lookupTable; /* Lookup table that maps glyphs to their + * baseline values. */ + public: + DEFINE_SIZE_MIN (68); +}; + +struct bsln +{ + static constexpr hb_tag_t tableTag = HB_AAT_TAG_bsln; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!(c->check_struct (this) && defaultBaseline < 32))) + return_trace (false); + + switch (format) + { + case 0: return_trace (parts.format0.sanitize (c)); + case 1: return_trace (parts.format1.sanitize (c)); + case 2: return_trace (parts.format2.sanitize (c)); + case 3: return_trace (parts.format3.sanitize (c)); + default:return_trace (true); + } + } + + protected: + FixedVersion<>version; /* Version number of the Baseline table. */ + HBUINT16 format; /* Format of the baseline table. Only one baseline + * format may be selected for the font. */ + HBUINT16 defaultBaseline;/* Default baseline value for all glyphs. + * This value can be from 0 through 31. */ + union { + // Distance-Based Formats + BaselineTableFormat0Part format0; + BaselineTableFormat1Part format1; + // Control Point-based Formats + BaselineTableFormat2Part format2; + BaselineTableFormat3Part format3; + } parts; + public: + DEFINE_SIZE_MIN (8); +}; + +} /* namespace AAT */ + + +#endif /* HB_AAT_LAYOUT_BSLN_TABLE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-common.hh b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-common.hh new file mode 100644 index 00000000000..672f80e0db8 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-common.hh @@ -0,0 +1,917 @@ +/* + * Copyright © 2017 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): Behdad Esfahbod + */ + +#ifndef HB_AAT_LAYOUT_COMMON_HH +#define HB_AAT_LAYOUT_COMMON_HH + +#include "hb-aat-layout.hh" +#include "hb-aat-map.hh" +#include "hb-open-type.hh" + +namespace OT { +struct GDEF; +}; + +namespace AAT { + +using namespace OT; + + +struct ankr; + +struct hb_aat_apply_context_t : + hb_dispatch_context_t +{ + const char *get_name () { return "APPLY"; } + template + return_t dispatch (const T &obj) { return obj.apply (this); } + static return_t default_return_value () { return false; } + bool stop_sublookup_iteration (return_t r) const { return r; } + + const hb_ot_shape_plan_t *plan; + hb_font_t *font; + hb_face_t *face; + hb_buffer_t *buffer; + hb_sanitize_context_t sanitizer; + const ankr *ankr_table; + const OT::GDEF *gdef_table; + const hb_sorted_vector_t *range_flags = nullptr; + hb_mask_t subtable_flags = 0; + + /* Unused. For debug tracing only. */ + unsigned int lookup_index; + + HB_INTERNAL hb_aat_apply_context_t (const hb_ot_shape_plan_t *plan_, + hb_font_t *font_, + hb_buffer_t *buffer_, + hb_blob_t *blob = const_cast (&Null (hb_blob_t))); + + HB_INTERNAL ~hb_aat_apply_context_t (); + + HB_INTERNAL void set_ankr_table (const AAT::ankr *ankr_table_); + + void set_lookup_index (unsigned int i) { lookup_index = i; } +}; + + +/* + * Lookup Table + */ + +template struct Lookup; + +template +struct LookupFormat0 +{ + friend struct Lookup; + + private: + const T* get_value (hb_codepoint_t glyph_id, unsigned int num_glyphs) const + { + if (unlikely (glyph_id >= num_glyphs)) return nullptr; + return &arrayZ[glyph_id]; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (arrayZ.sanitize (c, c->get_num_glyphs ())); + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (arrayZ.sanitize (c, c->get_num_glyphs (), base)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 0 */ + UnsizedArrayOf + arrayZ; /* Array of lookup values, indexed by glyph index. */ + public: + DEFINE_SIZE_UNBOUNDED (2); +}; + + +template +struct LookupSegmentSingle +{ + static constexpr unsigned TerminationWordCount = 2u; + + int cmp (hb_codepoint_t g) const + { return g < first ? -1 : g <= last ? 0 : +1 ; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && value.sanitize (c)); + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && value.sanitize (c, base)); + } + + HBGlyphID16 last; /* Last GlyphID in this segment */ + HBGlyphID16 first; /* First GlyphID in this segment */ + T value; /* The lookup value (only one) */ + public: + DEFINE_SIZE_STATIC (4 + T::static_size); +}; + +template +struct LookupFormat2 +{ + friend struct Lookup; + + private: + const T* get_value (hb_codepoint_t glyph_id) const + { + const LookupSegmentSingle *v = segments.bsearch (glyph_id); + return v ? &v->value : nullptr; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (segments.sanitize (c)); + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (segments.sanitize (c, base)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 2 */ + VarSizedBinSearchArrayOf> + segments; /* The actual segments. These must already be sorted, + * according to the first word in each one (the last + * glyph in each segment). */ + public: + DEFINE_SIZE_ARRAY (8, segments); +}; + +template +struct LookupSegmentArray +{ + static constexpr unsigned TerminationWordCount = 2u; + + const T* get_value (hb_codepoint_t glyph_id, const void *base) const + { + return first <= glyph_id && glyph_id <= last ? &(base+valuesZ)[glyph_id - first] : nullptr; + } + + int cmp (hb_codepoint_t g) const + { return g < first ? -1 : g <= last ? 0 : +1; } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + first <= last && + valuesZ.sanitize (c, base, last - first + 1)); + } + template + bool sanitize (hb_sanitize_context_t *c, const void *base, Ts&&... ds) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + first <= last && + valuesZ.sanitize (c, base, last - first + 1, std::forward (ds)...)); + } + + HBGlyphID16 last; /* Last GlyphID in this segment */ + HBGlyphID16 first; /* First GlyphID in this segment */ + NNOffset16To> + valuesZ; /* A 16-bit offset from the start of + * the table to the data. */ + public: + DEFINE_SIZE_STATIC (6); +}; + +template +struct LookupFormat4 +{ + friend struct Lookup; + + private: + const T* get_value (hb_codepoint_t glyph_id) const + { + const LookupSegmentArray *v = segments.bsearch (glyph_id); + return v ? v->get_value (glyph_id, this) : nullptr; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (segments.sanitize (c, this)); + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (segments.sanitize (c, this, base)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 4 */ + VarSizedBinSearchArrayOf> + segments; /* The actual segments. These must already be sorted, + * according to the first word in each one (the last + * glyph in each segment). */ + public: + DEFINE_SIZE_ARRAY (8, segments); +}; + +template +struct LookupSingle +{ + static constexpr unsigned TerminationWordCount = 1u; + + int cmp (hb_codepoint_t g) const { return glyph.cmp (g); } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && value.sanitize (c)); + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && value.sanitize (c, base)); + } + + HBGlyphID16 glyph; /* Last GlyphID */ + T value; /* The lookup value (only one) */ + public: + DEFINE_SIZE_STATIC (2 + T::static_size); +}; + +template +struct LookupFormat6 +{ + friend struct Lookup; + + private: + const T* get_value (hb_codepoint_t glyph_id) const + { + const LookupSingle *v = entries.bsearch (glyph_id); + return v ? &v->value : nullptr; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (entries.sanitize (c)); + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (entries.sanitize (c, base)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 6 */ + VarSizedBinSearchArrayOf> + entries; /* The actual entries, sorted by glyph index. */ + public: + DEFINE_SIZE_ARRAY (8, entries); +}; + +template +struct LookupFormat8 +{ + friend struct Lookup; + + private: + const T* get_value (hb_codepoint_t glyph_id) const + { + return firstGlyph <= glyph_id && glyph_id - firstGlyph < glyphCount ? + &valueArrayZ[glyph_id - firstGlyph] : nullptr; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && valueArrayZ.sanitize (c, glyphCount)); + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && valueArrayZ.sanitize (c, glyphCount, base)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 8 */ + HBGlyphID16 firstGlyph; /* First glyph index included in the trimmed array. */ + HBUINT16 glyphCount; /* Total number of glyphs (equivalent to the last + * glyph minus the value of firstGlyph plus 1). */ + UnsizedArrayOf + valueArrayZ; /* The lookup values (indexed by the glyph index + * minus the value of firstGlyph). */ + public: + DEFINE_SIZE_ARRAY (6, valueArrayZ); +}; + +template +struct LookupFormat10 +{ + friend struct Lookup; + + private: + const typename T::type get_value_or_null (hb_codepoint_t glyph_id) const + { + if (!(firstGlyph <= glyph_id && glyph_id - firstGlyph < glyphCount)) + return Null (T); + + const HBUINT8 *p = &valueArrayZ[(glyph_id - firstGlyph) * valueSize]; + + unsigned int v = 0; + unsigned int count = valueSize; + for (unsigned int i = 0; i < count; i++) + v = (v << 8) | *p++; + + return v; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + valueSize <= 4 && + valueArrayZ.sanitize (c, glyphCount * valueSize)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 8 */ + HBUINT16 valueSize; /* Byte size of each value. */ + HBGlyphID16 firstGlyph; /* First glyph index included in the trimmed array. */ + HBUINT16 glyphCount; /* Total number of glyphs (equivalent to the last + * glyph minus the value of firstGlyph plus 1). */ + UnsizedArrayOf + valueArrayZ; /* The lookup values (indexed by the glyph index + * minus the value of firstGlyph). */ + public: + DEFINE_SIZE_ARRAY (8, valueArrayZ); +}; + +template +struct Lookup +{ + const T* get_value (hb_codepoint_t glyph_id, unsigned int num_glyphs) const + { + switch (u.format) { + case 0: return u.format0.get_value (glyph_id, num_glyphs); + case 2: return u.format2.get_value (glyph_id); + case 4: return u.format4.get_value (glyph_id); + case 6: return u.format6.get_value (glyph_id); + case 8: return u.format8.get_value (glyph_id); + default:return nullptr; + } + } + + const typename T::type get_value_or_null (hb_codepoint_t glyph_id, unsigned int num_glyphs) const + { + switch (u.format) { + /* Format 10 cannot return a pointer. */ + case 10: return u.format10.get_value_or_null (glyph_id); + default: + const T *v = get_value (glyph_id, num_glyphs); + return v ? *v : Null (T); + } + } + + typename T::type get_class (hb_codepoint_t glyph_id, + unsigned int num_glyphs, + unsigned int outOfRange) const + { + const T *v = get_value (glyph_id, num_glyphs); + return v ? *v : outOfRange; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.format.sanitize (c)) return_trace (false); + switch (u.format) { + case 0: return_trace (u.format0.sanitize (c)); + case 2: return_trace (u.format2.sanitize (c)); + case 4: return_trace (u.format4.sanitize (c)); + case 6: return_trace (u.format6.sanitize (c)); + case 8: return_trace (u.format8.sanitize (c)); + case 10: return_trace (u.format10.sanitize (c)); + default:return_trace (true); + } + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + if (!u.format.sanitize (c)) return_trace (false); + switch (u.format) { + case 0: return_trace (u.format0.sanitize (c, base)); + case 2: return_trace (u.format2.sanitize (c, base)); + case 4: return_trace (u.format4.sanitize (c, base)); + case 6: return_trace (u.format6.sanitize (c, base)); + case 8: return_trace (u.format8.sanitize (c, base)); + case 10: return_trace (false); /* We don't support format10 here currently. */ + default:return_trace (true); + } + } + + protected: + union { + HBUINT16 format; /* Format identifier */ + LookupFormat0 format0; + LookupFormat2 format2; + LookupFormat4 format4; + LookupFormat6 format6; + LookupFormat8 format8; + LookupFormat10 format10; + } u; + public: + DEFINE_SIZE_UNION (2, format); +}; +DECLARE_NULL_NAMESPACE_BYTES_TEMPLATE1 (AAT, Lookup, 2); + +enum { DELETED_GLYPH = 0xFFFF }; + +/* + * (Extended) State Table + */ + +template +struct Entry +{ + // This does seem like it's ever called. + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + /* Note, we don't recurse-sanitize data because we don't access it. + * That said, in our DEFINE_SIZE_STATIC we access T::static_size, + * which ensures that data has a simple sanitize(). To be determined + * if I need to remove that as well. + * + * HOWEVER! Because we are a template, our DEFINE_SIZE_STATIC + * assertion wouldn't be checked, hence the line below. */ + static_assert (T::static_size, ""); + + return_trace (c->check_struct (this)); + } + + public: + HBUINT16 newState; /* Byte offset from beginning of state table + * to the new state. Really?!?! Or just state + * number? The latter in morx for sure. */ + HBUINT16 flags; /* Table specific. */ + T data; /* Optional offsets to per-glyph tables. */ + public: + DEFINE_SIZE_STATIC (4 + T::static_size); +}; + +template <> +struct Entry +{ + // This does seem like it's ever called. + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + HBUINT16 newState; /* Byte offset from beginning of state table to the new state. */ + HBUINT16 flags; /* Table specific. */ + public: + DEFINE_SIZE_STATIC (4); +}; + +template +struct StateTable +{ + typedef typename Types::HBUINT HBUINT; + typedef typename Types::HBUSHORT HBUSHORT; + typedef typename Types::ClassTypeNarrow ClassType; + + enum State + { + STATE_START_OF_TEXT = 0, + STATE_START_OF_LINE = 1, + }; + enum Class + { + CLASS_END_OF_TEXT = 0, + CLASS_OUT_OF_BOUNDS = 1, + CLASS_DELETED_GLYPH = 2, + CLASS_END_OF_LINE = 3, + }; + + int new_state (unsigned int newState) const + { return Types::extended ? newState : ((int) newState - (int) stateArrayTable) / (int) nClasses; } + + unsigned int get_class (hb_codepoint_t glyph_id, unsigned int num_glyphs) const + { + if (unlikely (glyph_id == DELETED_GLYPH)) return CLASS_DELETED_GLYPH; + return (this+classTable).get_class (glyph_id, num_glyphs, 1); + } + + const Entry *get_entries () const + { return (this+entryTable).arrayZ; } + + const Entry &get_entry (int state, unsigned int klass) const + { + if (unlikely (klass >= nClasses)) + klass = StateTable::CLASS_OUT_OF_BOUNDS; + + const HBUSHORT *states = (this+stateArrayTable).arrayZ; + const Entry *entries = (this+entryTable).arrayZ; + + unsigned int entry = states[state * nClasses + klass]; + DEBUG_MSG (APPLY, nullptr, "e%u", entry); + + return entries[entry]; + } + + bool sanitize (hb_sanitize_context_t *c, + unsigned int *num_entries_out = nullptr) const + { + TRACE_SANITIZE (this); + if (unlikely (!(c->check_struct (this) && + nClasses >= 4 /* Ensure pre-defined classes fit. */ && + classTable.sanitize (c, this)))) return_trace (false); + + const HBUSHORT *states = (this+stateArrayTable).arrayZ; + const Entry *entries = (this+entryTable).arrayZ; + + unsigned int num_classes = nClasses; + if (unlikely (hb_unsigned_mul_overflows (num_classes, states[0].static_size))) + return_trace (false); + unsigned int row_stride = num_classes * states[0].static_size; + + /* Apple 'kern' table has this peculiarity: + * + * "Because the stateTableOffset in the state table header is (strictly + * speaking) redundant, some 'kern' tables use it to record an initial + * state where that should not be StartOfText. To determine if this is + * done, calculate what the stateTableOffset should be. If it's different + * from the actual stateTableOffset, use it as the initial state." + * + * We implement this by calling the initial state zero, but allow *negative* + * states if the start state indeed was not the first state. Since the code + * is shared, this will also apply to 'mort' table. The 'kerx' / 'morx' + * tables are not affected since those address states by index, not offset. + */ + + int min_state = 0; + int max_state = 0; + unsigned int num_entries = 0; + + int state_pos = 0; + int state_neg = 0; + unsigned int entry = 0; + while (min_state < state_neg || state_pos <= max_state) + { + if (min_state < state_neg) + { + /* Negative states. */ + if (unlikely (hb_unsigned_mul_overflows (min_state, num_classes))) + return_trace (false); + if (unlikely (!c->check_range (&states[min_state * num_classes], + -min_state, + row_stride))) + return_trace (false); + if ((c->max_ops -= state_neg - min_state) <= 0) + return_trace (false); + { /* Sweep new states. */ + const HBUSHORT *stop = &states[min_state * num_classes]; + if (unlikely (stop > states)) + return_trace (false); + for (const HBUSHORT *p = states; stop < p; p--) + num_entries = hb_max (num_entries, *(p - 1) + 1u); + state_neg = min_state; + } + } + + if (state_pos <= max_state) + { + /* Positive states. */ + if (unlikely (!c->check_range (states, + max_state + 1, + row_stride))) + return_trace (false); + if ((c->max_ops -= max_state - state_pos + 1) <= 0) + return_trace (false); + { /* Sweep new states. */ + if (unlikely (hb_unsigned_mul_overflows ((max_state + 1), num_classes))) + return_trace (false); + const HBUSHORT *stop = &states[(max_state + 1) * num_classes]; + if (unlikely (stop < states)) + return_trace (false); + for (const HBUSHORT *p = &states[state_pos * num_classes]; p < stop; p++) + num_entries = hb_max (num_entries, *p + 1u); + state_pos = max_state + 1; + } + } + + if (unlikely (!c->check_array (entries, num_entries))) + return_trace (false); + if ((c->max_ops -= num_entries - entry) <= 0) + return_trace (false); + { /* Sweep new entries. */ + const Entry *stop = &entries[num_entries]; + for (const Entry *p = &entries[entry]; p < stop; p++) + { + int newState = new_state (p->newState); + min_state = hb_min (min_state, newState); + max_state = hb_max (max_state, newState); + } + entry = num_entries; + } + } + + if (num_entries_out) + *num_entries_out = num_entries; + + return_trace (true); + } + + protected: + HBUINT nClasses; /* Number of classes, which is the number of indices + * in a single line in the state array. */ + NNOffsetTo + classTable; /* Offset to the class table. */ + NNOffsetTo, HBUINT> + stateArrayTable;/* Offset to the state array. */ + NNOffsetTo>, HBUINT> + entryTable; /* Offset to the entry array. */ + + public: + DEFINE_SIZE_STATIC (4 * sizeof (HBUINT)); +}; + +template +struct ClassTable +{ + unsigned int get_class (hb_codepoint_t glyph_id, unsigned int outOfRange) const + { + unsigned int i = glyph_id - firstGlyph; + return i >= classArray.len ? outOfRange : classArray.arrayZ[i]; + } + unsigned int get_class (hb_codepoint_t glyph_id, + unsigned int num_glyphs HB_UNUSED, + unsigned int outOfRange) const + { + return get_class (glyph_id, outOfRange); + } + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && classArray.sanitize (c)); + } + protected: + HBGlyphID16 firstGlyph; /* First glyph index included in the trimmed array. */ + Array16Of classArray; /* The class codes (indexed by glyph index minus + * firstGlyph). */ + public: + DEFINE_SIZE_ARRAY (4, classArray); +}; + +struct ObsoleteTypes +{ + static constexpr bool extended = false; + typedef HBUINT16 HBUINT; + typedef HBUINT8 HBUSHORT; + typedef ClassTable ClassTypeNarrow; + typedef ClassTable ClassTypeWide; + + template + static unsigned int offsetToIndex (unsigned int offset, + const void *base, + const T *array) + { + /* https://github.com/harfbuzz/harfbuzz/issues/3483 */ + /* If offset is less than base, return an offset that would + * result in an address half a 32bit address-space away, + * to make sure sanitize fails even on 32bit builds. */ + if (unlikely (offset < unsigned ((const char *) array - (const char *) base))) + return INT_MAX / T::static_size; + + /* https://github.com/harfbuzz/harfbuzz/issues/2816 */ + return (offset - unsigned ((const char *) array - (const char *) base)) / T::static_size; + } + template + static unsigned int byteOffsetToIndex (unsigned int offset, + const void *base, + const T *array) + { + return offsetToIndex (offset, base, array); + } + template + static unsigned int wordOffsetToIndex (unsigned int offset, + const void *base, + const T *array) + { + return offsetToIndex (2 * offset, base, array); + } +}; +struct ExtendedTypes +{ + static constexpr bool extended = true; + typedef HBUINT32 HBUINT; + typedef HBUINT16 HBUSHORT; + typedef Lookup ClassTypeNarrow; + typedef Lookup ClassTypeWide; + + template + static unsigned int offsetToIndex (unsigned int offset, + const void *base HB_UNUSED, + const T *array HB_UNUSED) + { + return offset; + } + template + static unsigned int byteOffsetToIndex (unsigned int offset, + const void *base HB_UNUSED, + const T *array HB_UNUSED) + { + return offset / 2; + } + template + static unsigned int wordOffsetToIndex (unsigned int offset, + const void *base HB_UNUSED, + const T *array HB_UNUSED) + { + return offset; + } +}; + +template +struct StateTableDriver +{ + using StateTableT = StateTable; + using EntryT = Entry; + + StateTableDriver (const StateTableT &machine_, + hb_buffer_t *buffer_, + hb_face_t *face_) : + machine (machine_), + buffer (buffer_), + num_glyphs (face_->get_num_glyphs ()) {} + + template + void drive (context_t *c, hb_aat_apply_context_t *ac) + { + if (!c->in_place) + buffer->clear_output (); + + int state = StateTableT::STATE_START_OF_TEXT; + // If there's only one range, we already checked the flag. + auto *last_range = ac->range_flags && (ac->range_flags->length > 1) ? &(*ac->range_flags)[0] : nullptr; + for (buffer->idx = 0; buffer->successful;) + { + /* This block is copied in NoncontextualSubtable::apply. Keep in sync. */ + if (last_range) + { + auto *range = last_range; + if (buffer->idx < buffer->len) + { + unsigned cluster = buffer->cur().cluster; + while (cluster < range->cluster_first) + range--; + while (cluster > range->cluster_last) + range++; + + + last_range = range; + } + if (!(range->flags & ac->subtable_flags)) + { + if (buffer->idx == buffer->len || unlikely (!buffer->successful)) + break; + + state = StateTableT::STATE_START_OF_TEXT; + (void) buffer->next_glyph (); + continue; + } + } + + unsigned int klass = buffer->idx < buffer->len ? + machine.get_class (buffer->cur().codepoint, num_glyphs) : + (unsigned) StateTableT::CLASS_END_OF_TEXT; + DEBUG_MSG (APPLY, nullptr, "c%u at %u", klass, buffer->idx); + const EntryT &entry = machine.get_entry (state, klass); + const int next_state = machine.new_state (entry.newState); + + /* Conditions under which it's guaranteed safe-to-break before current glyph: + * + * 1. There was no action in this transition; and + * + * 2. If we break before current glyph, the results will be the same. That + * is guaranteed if: + * + * 2a. We were already in start-of-text state; or + * + * 2b. We are epsilon-transitioning to start-of-text state; or + * + * 2c. Starting from start-of-text state seeing current glyph: + * + * 2c'. There won't be any actions; and + * + * 2c". We would end up in the same state that we were going to end up + * in now, including whether epsilon-transitioning. + * + * and + * + * 3. If we break before current glyph, there won't be any end-of-text action + * after previous glyph. + * + * This triples the transitions we need to look up, but is worth returning + * granular unsafe-to-break results. See eg.: + * + * https://github.com/harfbuzz/harfbuzz/issues/2860 + */ + + const auto is_safe_to_break_extra = [&]() + { + /* 2c. */ + const auto wouldbe_entry = machine.get_entry(StateTableT::STATE_START_OF_TEXT, klass); + + /* 2c'. */ + if (c->is_actionable (this, wouldbe_entry)) + return false; + + /* 2c". */ + return next_state == machine.new_state(wouldbe_entry.newState) + && (entry.flags & context_t::DontAdvance) == (wouldbe_entry.flags & context_t::DontAdvance); + }; + + const auto is_safe_to_break = [&]() + { + /* 1. */ + if (c->is_actionable (this, entry)) + return false; + + /* 2. */ + // This one is meh, I know... + const auto ok = + state == StateTableT::STATE_START_OF_TEXT + || ((entry.flags & context_t::DontAdvance) && next_state == StateTableT::STATE_START_OF_TEXT) + || is_safe_to_break_extra(); + if (!ok) + return false; + + /* 3. */ + return !c->is_actionable (this, machine.get_entry (state, StateTableT::CLASS_END_OF_TEXT)); + }; + + if (!is_safe_to_break () && buffer->backtrack_len () && buffer->idx < buffer->len) + buffer->unsafe_to_break_from_outbuffer (buffer->backtrack_len () - 1, buffer->idx + 1); + + c->transition (this, entry); + + state = next_state; + DEBUG_MSG (APPLY, nullptr, "s%d", state); + + if (buffer->idx == buffer->len || unlikely (!buffer->successful)) + break; + + if (!(entry.flags & context_t::DontAdvance) || buffer->max_ops-- <= 0) + (void) buffer->next_glyph (); + } + + if (!c->in_place) + buffer->sync (); + } + + public: + const StateTableT &machine; + hb_buffer_t *buffer; + unsigned int num_glyphs; +}; + + +} /* namespace AAT */ + + +#endif /* HB_AAT_LAYOUT_COMMON_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-feat-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-feat-table.hh new file mode 100644 index 00000000000..815a1fd2aa9 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-feat-table.hh @@ -0,0 +1,222 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * + * 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. + */ + +#ifndef HB_AAT_LAYOUT_FEAT_TABLE_HH +#define HB_AAT_LAYOUT_FEAT_TABLE_HH + +#include "hb-aat-layout-common.hh" + +/* + * feat -- Feature Name + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6feat.html + */ +#define HB_AAT_TAG_feat HB_TAG('f','e','a','t') + + +namespace AAT { + + +struct SettingName +{ + friend struct FeatureName; + + int cmp (hb_aat_layout_feature_selector_t key) const + { return (int) key - (int) setting; } + + hb_aat_layout_feature_selector_t get_selector () const + { return (hb_aat_layout_feature_selector_t) (unsigned) setting; } + + hb_aat_layout_feature_selector_info_t get_info (hb_aat_layout_feature_selector_t default_selector) const + { + return { + nameIndex, + (hb_aat_layout_feature_selector_t) (unsigned int) setting, + default_selector == HB_AAT_LAYOUT_FEATURE_SELECTOR_INVALID + ? (hb_aat_layout_feature_selector_t) (setting + 1) + : default_selector, + 0 + }; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + HBUINT16 setting; /* The setting. */ + NameID nameIndex; /* The name table index for the setting's name. */ + public: + DEFINE_SIZE_STATIC (4); +}; +DECLARE_NULL_NAMESPACE_BYTES (AAT, SettingName); + +struct feat; + +struct FeatureName +{ + int cmp (hb_aat_layout_feature_type_t key) const + { return (int) key - (int) feature; } + + enum { + Exclusive = 0x8000, /* If set, the feature settings are mutually exclusive. */ + NotDefault = 0x4000, /* If clear, then the setting with an index of 0 in + * the setting name array for this feature should + * be taken as the default for the feature + * (if one is required). If set, then bits 0-15 of this + * featureFlags field contain the index of the setting + * which is to be taken as the default. */ + IndexMask = 0x00FF /* If bits 30 and 31 are set, then these sixteen bits + * indicate the index of the setting in the setting name + * array for this feature which should be taken + * as the default. */ + }; + + unsigned int get_selector_infos (unsigned int start_offset, + unsigned int *selectors_count, /* IN/OUT. May be NULL. */ + hb_aat_layout_feature_selector_info_t *selectors, /* OUT. May be NULL. */ + unsigned int *pdefault_index, /* OUT. May be NULL. */ + const void *base) const + { + hb_array_t< const SettingName> settings_table = (base+settingTableZ).as_array (nSettings); + + static_assert (Index::NOT_FOUND_INDEX == HB_AAT_LAYOUT_NO_SELECTOR_INDEX, ""); + + hb_aat_layout_feature_selector_t default_selector = HB_AAT_LAYOUT_FEATURE_SELECTOR_INVALID; + unsigned int default_index = Index::NOT_FOUND_INDEX; + if (featureFlags & Exclusive) + { + default_index = (featureFlags & NotDefault) ? featureFlags & IndexMask : 0; + default_selector = settings_table[default_index].get_selector (); + } + if (pdefault_index) + *pdefault_index = default_index; + + if (selectors_count) + { + + settings_table.sub_array (start_offset, selectors_count) + | hb_map ([=] (const SettingName& setting) { return setting.get_info (default_selector); }) + | hb_sink (hb_array (selectors, *selectors_count)) + ; + } + return settings_table.length; + } + + hb_aat_layout_feature_type_t get_feature_type () const + { return (hb_aat_layout_feature_type_t) (unsigned int) feature; } + + hb_ot_name_id_t get_feature_name_id () const { return nameIndex; } + + bool is_exclusive () const { return featureFlags & Exclusive; } + + /* A FeatureName with no settings is meaningless */ + bool has_data () const { return nSettings; } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + (base+settingTableZ).sanitize (c, nSettings))); + } + + protected: + HBUINT16 feature; /* Feature type. */ + HBUINT16 nSettings; /* The number of records in the setting name array. */ + NNOffset32To> + settingTableZ; /* Offset in bytes from the beginning of this table to + * this feature's setting name array. The actual type of + * record this offset refers to will depend on the + * exclusivity value, as described below. */ + HBUINT16 featureFlags; /* Single-bit flags associated with the feature type. */ + HBINT16 nameIndex; /* The name table index for the feature's name. + * This index has values greater than 255 and + * less than 32768. */ + public: + DEFINE_SIZE_STATIC (12); +}; + +struct feat +{ + static constexpr hb_tag_t tableTag = HB_AAT_TAG_feat; + + bool has_data () const { return version.to_int (); } + + unsigned int get_feature_types (unsigned int start_offset, + unsigned int *count, + hb_aat_layout_feature_type_t *features) const + { + if (count) + { + + namesZ.as_array (featureNameCount).sub_array (start_offset, count) + | hb_map (&FeatureName::get_feature_type) + | hb_sink (hb_array (features, *count)) + ; + } + return featureNameCount; + } + + bool exposes_feature (hb_aat_layout_feature_type_t feature_type) const + { return get_feature (feature_type).has_data (); } + + const FeatureName& get_feature (hb_aat_layout_feature_type_t feature_type) const + { return namesZ.bsearch (featureNameCount, feature_type); } + + hb_ot_name_id_t get_feature_name_id (hb_aat_layout_feature_type_t feature) const + { return get_feature (feature).get_feature_name_id (); } + + unsigned int get_selector_infos (hb_aat_layout_feature_type_t feature_type, + unsigned int start_offset, + unsigned int *selectors_count, /* IN/OUT. May be NULL. */ + hb_aat_layout_feature_selector_info_t *selectors, /* OUT. May be NULL. */ + unsigned int *default_index /* OUT. May be NULL. */) const + { + return get_feature (feature_type).get_selector_infos (start_offset, selectors_count, selectors, + default_index, this); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + version.major == 1 && + namesZ.sanitize (c, featureNameCount, this))); + } + + protected: + FixedVersion<>version; /* Version number of the feature name table + * (0x00010000 for the current version). */ + HBUINT16 featureNameCount; + /* The number of entries in the feature name array. */ + HBUINT16 reserved1; /* Reserved (set to zero). */ + HBUINT32 reserved2; /* Reserved (set to zero). */ + SortedUnsizedArrayOf + namesZ; /* The feature name array. */ + public: + DEFINE_SIZE_ARRAY (12, namesZ); +}; + +} /* namespace AAT */ + +#endif /* HB_AAT_LAYOUT_FEAT_TABLE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-just-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-just-table.hh new file mode 100644 index 00000000000..8fd3990f887 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-just-table.hh @@ -0,0 +1,417 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * + * 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. + */ + +#ifndef HB_AAT_LAYOUT_JUST_TABLE_HH +#define HB_AAT_LAYOUT_JUST_TABLE_HH + +#include "hb-aat-layout-common.hh" +#include "hb-ot-layout.hh" +#include "hb-open-type.hh" + +#include "hb-aat-layout-morx-table.hh" + +/* + * just -- Justification + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6just.html + */ +#define HB_AAT_TAG_just HB_TAG('j','u','s','t') + + +namespace AAT { + +using namespace OT; + + +struct ActionSubrecordHeader +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + HBUINT16 actionClass; /* The JustClass value associated with this + * ActionSubrecord. */ + HBUINT16 actionType; /* The type of postcompensation action. */ + HBUINT16 actionLength; /* Length of this ActionSubrecord record, which + * must be a multiple of 4. */ + public: + DEFINE_SIZE_STATIC (6); +}; + +struct DecompositionAction +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + ActionSubrecordHeader + header; + F16DOT16 lowerLimit; /* If the distance factor is less than this value, + * then the ligature is decomposed. */ + F16DOT16 upperLimit; /* If the distance factor is greater than this value, + * then the ligature is decomposed. */ + HBUINT16 order; /* Numerical order in which this ligature will + * be decomposed; you may want infrequent ligatures + * to decompose before more frequent ones. The ligatures + * on the line of text will decompose in increasing + * value of this field. */ + Array16Of + decomposedglyphs; + /* Number of 16-bit glyph indexes that follow; + * the ligature will be decomposed into these glyphs. + * + * Array of decomposed glyphs. */ + public: + DEFINE_SIZE_ARRAY (18, decomposedglyphs); +}; + +struct UnconditionalAddGlyphAction +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + ActionSubrecordHeader + header; + HBGlyphID16 addGlyph; /* Glyph that should be added if the distance factor + * is growing. */ + + public: + DEFINE_SIZE_STATIC (8); +}; + +struct ConditionalAddGlyphAction +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + ActionSubrecordHeader + header; + F16DOT16 substThreshold; /* Distance growth factor (in ems) at which + * this glyph is replaced and the growth factor + * recalculated. */ + HBGlyphID16 addGlyph; /* Glyph to be added as kashida. If this value is + * 0xFFFF, no extra glyph will be added. Note that + * generally when a glyph is added, justification + * will need to be redone. */ + HBGlyphID16 substGlyph; /* Glyph to be substituted for this glyph if the + * growth factor equals or exceeds the value of + * substThreshold. */ + public: + DEFINE_SIZE_STATIC (14); +}; + +struct DuctileGlyphAction +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + ActionSubrecordHeader + header; + HBUINT32 variationAxis; /* The 4-byte tag identifying the ductile axis. + * This would normally be 0x64756374 ('duct'), + * but you may use any axis the font contains. */ + F16DOT16 minimumLimit; /* The lowest value for the ductility axis that + * still yields an acceptable appearance. Normally + * this will be 1.0. */ + F16DOT16 noStretchValue; /* This is the default value that corresponds to + * no change in appearance. Normally, this will + * be 1.0. */ + F16DOT16 maximumLimit; /* The highest value for the ductility axis that + * still yields an acceptable appearance. */ + public: + DEFINE_SIZE_STATIC (22); +}; + +struct RepeatedAddGlyphAction +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + ActionSubrecordHeader + header; + HBUINT16 flags; /* Currently unused; set to 0. */ + HBGlyphID16 glyph; /* Glyph that should be added if the distance factor + * is growing. */ + public: + DEFINE_SIZE_STATIC (10); +}; + +struct ActionSubrecord +{ + unsigned int get_length () const { return u.header.actionLength; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + + switch (u.header.actionType) + { + case 0: return_trace (u.decompositionAction.sanitize (c)); + case 1: return_trace (u.unconditionalAddGlyphAction.sanitize (c)); + case 2: return_trace (u.conditionalAddGlyphAction.sanitize (c)); + // case 3: return_trace (u.stretchGlyphAction.sanitize (c)); + case 4: return_trace (u.decompositionAction.sanitize (c)); + case 5: return_trace (u.decompositionAction.sanitize (c)); + default: return_trace (true); + } + } + + protected: + union { + ActionSubrecordHeader header; + DecompositionAction decompositionAction; + UnconditionalAddGlyphAction unconditionalAddGlyphAction; + ConditionalAddGlyphAction conditionalAddGlyphAction; + /* StretchGlyphAction stretchGlyphAction; -- Not supported by CoreText */ + DuctileGlyphAction ductileGlyphAction; + RepeatedAddGlyphAction repeatedAddGlyphAction; + } u; /* Data. The format of this data depends on + * the value of the actionType field. */ + public: + DEFINE_SIZE_UNION (6, header); +}; + +struct PostcompensationActionChain +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + + unsigned int offset = min_size; + for (unsigned int i = 0; i < count; i++) + { + const ActionSubrecord& subrecord = StructAtOffset (this, offset); + if (unlikely (!subrecord.sanitize (c))) return_trace (false); + offset += subrecord.get_length (); + } + + return_trace (true); + } + + protected: + HBUINT32 count; + + public: + DEFINE_SIZE_STATIC (4); +}; + +struct JustWidthDeltaEntry +{ + enum Flags + { + Reserved1 =0xE000,/* Reserved. You should set these bits to zero. */ + UnlimiteGap =0x1000,/* The glyph can take unlimited gap. When this + * glyph participates in the justification process, + * it and any other glyphs on the line having this + * bit set absorb all the remaining gap. */ + Reserved2 =0x0FF0,/* Reserved. You should set these bits to zero. */ + Priority =0x000F /* The justification priority of the glyph. */ + }; + + enum Priority + { + Kashida = 0, /* Kashida priority. This is the highest priority + * during justification. */ + Whitespace = 1, /* Whitespace priority. Any whitespace glyphs (as + * identified in the glyph properties table) will + * get this priority. */ + InterCharacter = 2, /* Inter-character priority. Give this to any + * remaining glyphs. */ + NullPriority = 3 /* Null priority. You should set this priority for + * glyphs that only participate in justification + * after the above priorities. Normally all glyphs + * have one of the previous three values. If you + * don't want a glyph to participate in justification, + * and you don't want to set its factors to zero, + * you may instead assign it to the null priority. */ + }; + + protected: + F16DOT16 beforeGrowLimit;/* The ratio by which the advance width of the + * glyph is permitted to grow on the left or top side. */ + F16DOT16 beforeShrinkLimit; + /* The ratio by which the advance width of the + * glyph is permitted to shrink on the left or top side. */ + F16DOT16 afterGrowLimit; /* The ratio by which the advance width of the glyph + * is permitted to shrink on the left or top side. */ + F16DOT16 afterShrinkLimit; + /* The ratio by which the advance width of the glyph + * is at most permitted to shrink on the right or + * bottom side. */ + HBUINT16 growFlags; /* Flags controlling the grow case. */ + HBUINT16 shrinkFlags; /* Flags controlling the shrink case. */ + + public: + DEFINE_SIZE_STATIC (20); +}; + +struct WidthDeltaPair +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + HBUINT32 justClass; /* The justification category associated + * with the wdRecord field. Only 7 bits of + * this field are used. (The other bits are + * used as padding to guarantee longword + * alignment of the following record). */ + JustWidthDeltaEntry + wdRecord; /* The actual width delta record. */ + + public: + DEFINE_SIZE_STATIC (24); +}; + +typedef OT::Array32Of WidthDeltaCluster; + +struct JustificationCategory +{ + typedef void EntryData; + + enum Flags + { + SetMark =0x8000,/* If set, make the current glyph the marked + * glyph. */ + DontAdvance =0x4000,/* If set, don't advance to the next glyph before + * going to the new state. */ + MarkCategory =0x3F80,/* The justification category for the marked + * glyph if nonzero. */ + CurrentCategory =0x007F /* The justification category for the current + * glyph if nonzero. */ + }; + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + morphHeader.sanitize (c) && + stHeader.sanitize (c))); + } + + protected: + ChainSubtable + morphHeader; /* Metamorphosis-style subtable header. */ + StateTable + stHeader; /* The justification insertion state table header */ + public: + DEFINE_SIZE_STATIC (30); +}; + +struct JustificationHeader +{ + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + justClassTable.sanitize (c, base, base) && + wdcTable.sanitize (c, base) && + pcTable.sanitize (c, base) && + lookupTable.sanitize (c, base))); + } + + protected: + Offset16To + justClassTable; /* Offset to the justification category state table. */ + Offset16To + wdcTable; /* Offset from start of justification table to start + * of the subtable containing the width delta factors + * for the glyphs in your font. + * + * The width delta clusters table. */ + Offset16To + pcTable; /* Offset from start of justification table to start + * of postcompensation subtable (set to zero if none). + * + * The postcompensation subtable, if present in the font. */ + Lookup> + lookupTable; /* Lookup table associating glyphs with width delta + * clusters. See the description of Width Delta Clusters + * table for details on how to interpret the lookup values. */ + + public: + DEFINE_SIZE_MIN (8); +}; + +struct just +{ + static constexpr hb_tag_t tableTag = HB_AAT_TAG_just; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + + return_trace (likely (c->check_struct (this) && + version.major == 1 && + horizData.sanitize (c, this, this) && + vertData.sanitize (c, this, this))); + } + + protected: + FixedVersion<>version; /* Version of the justification table + * (0x00010000u for version 1.0). */ + HBUINT16 format; /* Format of the justification table (set to 0). */ + Offset16To + horizData; /* Byte offset from the start of the justification table + * to the header for tables that contain justification + * information for horizontal text. + * If you are not including this information, + * store 0. */ + Offset16To + vertData; /* ditto, vertical */ + + public: + DEFINE_SIZE_STATIC (10); +}; + +} /* namespace AAT */ + + +#endif /* HB_AAT_LAYOUT_JUST_TABLE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-kerx-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-kerx-table.hh new file mode 100644 index 00000000000..35d7c84c2bc --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-kerx-table.hh @@ -0,0 +1,1001 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * 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): Behdad Esfahbod + */ + +#ifndef HB_AAT_LAYOUT_KERX_TABLE_HH +#define HB_AAT_LAYOUT_KERX_TABLE_HH + +#include "hb-kern.hh" +#include "hb-aat-layout-ankr-table.hh" + +/* + * kerx -- Extended Kerning + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6kerx.html + */ +#define HB_AAT_TAG_kerx HB_TAG('k','e','r','x') + + +namespace AAT { + +using namespace OT; + + +static inline int +kerxTupleKern (int value, + unsigned int tupleCount, + const void *base, + hb_aat_apply_context_t *c) +{ + if (likely (!tupleCount || !c)) return value; + + unsigned int offset = value; + const FWORD *pv = &StructAtOffset (base, offset); + if (unlikely (!c->sanitizer.check_array (pv, tupleCount))) return 0; + return *pv; +} + + +struct hb_glyph_pair_t +{ + hb_codepoint_t left; + hb_codepoint_t right; +}; + +struct KernPair +{ + int get_kerning () const { return value; } + + int cmp (const hb_glyph_pair_t &o) const + { + int ret = left.cmp (o.left); + if (ret) return ret; + return right.cmp (o.right); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + HBGlyphID16 left; + HBGlyphID16 right; + FWORD value; + public: + DEFINE_SIZE_STATIC (6); +}; + +template +struct KerxSubTableFormat0 +{ + int get_kerning (hb_codepoint_t left, hb_codepoint_t right, + hb_aat_apply_context_t *c = nullptr) const + { + hb_glyph_pair_t pair = {left, right}; + int v = pairs.bsearch (pair).get_kerning (); + return kerxTupleKern (v, header.tuple_count (), this, c); + } + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + if (!c->plan->requested_kerning) + return false; + + if (header.coverage & header.Backwards) + return false; + + accelerator_t accel (*this, c); + hb_kern_machine_t machine (accel, header.coverage & header.CrossStream); + machine.kern (c->font, c->buffer, c->plan->kern_mask); + + return_trace (true); + } + + struct accelerator_t + { + const KerxSubTableFormat0 &table; + hb_aat_apply_context_t *c; + + accelerator_t (const KerxSubTableFormat0 &table_, + hb_aat_apply_context_t *c_) : + table (table_), c (c_) {} + + int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const + { return table.get_kerning (left, right, c); } + }; + + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (pairs.sanitize (c))); + } + + protected: + KernSubTableHeader header; + BinSearchArrayOf + pairs; /* Sorted kern records. */ + public: + DEFINE_SIZE_ARRAY (KernSubTableHeader::static_size + 16, pairs); +}; + + +template +struct Format1Entry; + +template <> +struct Format1Entry +{ + enum Flags + { + Push = 0x8000, /* If set, push this glyph on the kerning stack. */ + DontAdvance = 0x4000, /* If set, don't advance to the next glyph + * before going to the new state. */ + Reset = 0x2000, /* If set, reset the kerning data (clear the stack) */ + Reserved = 0x1FFF, /* Not used; set to 0. */ + }; + + struct EntryData + { + HBUINT16 kernActionIndex;/* Index into the kerning value array. If + * this index is 0xFFFF, then no kerning + * is to be performed. */ + public: + DEFINE_SIZE_STATIC (2); + }; + + static bool performAction (const Entry &entry) + { return entry.data.kernActionIndex != 0xFFFF; } + + static unsigned int kernActionIndex (const Entry &entry) + { return entry.data.kernActionIndex; } +}; +template <> +struct Format1Entry +{ + enum Flags + { + Push = 0x8000, /* If set, push this glyph on the kerning stack. */ + DontAdvance = 0x4000, /* If set, don't advance to the next glyph + * before going to the new state. */ + Offset = 0x3FFF, /* Byte offset from beginning of subtable to the + * value table for the glyphs on the kerning stack. */ + + Reset = 0x0000, /* Not supported? */ + }; + + typedef void EntryData; + + static bool performAction (const Entry &entry) + { return entry.flags & Offset; } + + static unsigned int kernActionIndex (const Entry &entry) + { return entry.flags & Offset; } +}; + +template +struct KerxSubTableFormat1 +{ + typedef typename KernSubTableHeader::Types Types; + typedef typename Types::HBUINT HBUINT; + + typedef Format1Entry Format1EntryT; + typedef typename Format1EntryT::EntryData EntryData; + + struct driver_context_t + { + static constexpr bool in_place = true; + enum + { + DontAdvance = Format1EntryT::DontAdvance, + }; + + driver_context_t (const KerxSubTableFormat1 *table_, + hb_aat_apply_context_t *c_) : + c (c_), + table (table_), + /* Apparently the offset kernAction is from the beginning of the state-machine, + * similar to offsets in morx table, NOT from beginning of this table, like + * other subtables in kerx. Discovered via testing. */ + kernAction (&table->machine + table->kernAction), + depth (0), + crossStream (table->header.coverage & table->header.CrossStream) {} + + bool is_actionable (StateTableDriver *driver HB_UNUSED, + const Entry &entry) + { return Format1EntryT::performAction (entry); } + void transition (StateTableDriver *driver, + const Entry &entry) + { + hb_buffer_t *buffer = driver->buffer; + unsigned int flags = entry.flags; + + if (flags & Format1EntryT::Reset) + depth = 0; + + if (flags & Format1EntryT::Push) + { + if (likely (depth < ARRAY_LENGTH (stack))) + stack[depth++] = buffer->idx; + else + depth = 0; /* Probably not what CoreText does, but better? */ + } + + if (Format1EntryT::performAction (entry) && depth) + { + unsigned int tuple_count = hb_max (1u, table->header.tuple_count ()); + + unsigned int kern_idx = Format1EntryT::kernActionIndex (entry); + kern_idx = Types::byteOffsetToIndex (kern_idx, &table->machine, kernAction.arrayZ); + const FWORD *actions = &kernAction[kern_idx]; + if (!c->sanitizer.check_array (actions, depth, tuple_count)) + { + depth = 0; + return; + } + + hb_mask_t kern_mask = c->plan->kern_mask; + + /* From Apple 'kern' spec: + * "Each pops one glyph from the kerning stack and applies the kerning value to it. + * The end of the list is marked by an odd value... */ + bool last = false; + while (!last && depth) + { + unsigned int idx = stack[--depth]; + int v = *actions; + actions += tuple_count; + if (idx >= buffer->len) continue; + + /* "The end of the list is marked by an odd value..." */ + last = v & 1; + v &= ~1; + + hb_glyph_position_t &o = buffer->pos[idx]; + + if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction)) + { + if (crossStream) + { + /* The following flag is undocumented in the spec, but described + * in the 'kern' table example. */ + if (v == -0x8000) + { + o.attach_type() = OT::Layout::GPOS_impl::ATTACH_TYPE_NONE; + o.attach_chain() = 0; + o.y_offset = 0; + } + else if (o.attach_type()) + { + o.y_offset += c->font->em_scale_y (v); + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; + } + } + else if (buffer->info[idx].mask & kern_mask) + { + o.x_advance += c->font->em_scale_x (v); + o.x_offset += c->font->em_scale_x (v); + } + } + else + { + if (crossStream) + { + /* CoreText doesn't do crossStream kerning in vertical. We do. */ + if (v == -0x8000) + { + o.attach_type() = OT::Layout::GPOS_impl::ATTACH_TYPE_NONE; + o.attach_chain() = 0; + o.x_offset = 0; + } + else if (o.attach_type()) + { + o.x_offset += c->font->em_scale_x (v); + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; + } + } + else if (buffer->info[idx].mask & kern_mask) + { + o.y_advance += c->font->em_scale_y (v); + o.y_offset += c->font->em_scale_y (v); + } + } + } + } + } + + private: + hb_aat_apply_context_t *c; + const KerxSubTableFormat1 *table; + const UnsizedArrayOf &kernAction; + unsigned int stack[8]; + unsigned int depth; + bool crossStream; + }; + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + if (!c->plan->requested_kerning && + !(header.coverage & header.CrossStream)) + return false; + + driver_context_t dc (this, c); + + StateTableDriver driver (machine, c->buffer, c->font->face); + driver.drive (&dc, c); + + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + /* The rest of array sanitizations are done at run-time. */ + return_trace (likely (c->check_struct (this) && + machine.sanitize (c))); + } + + protected: + KernSubTableHeader header; + StateTable machine; + NNOffsetTo, HBUINT> kernAction; + public: + DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 5 * sizeof (HBUINT)); +}; + +template +struct KerxSubTableFormat2 +{ + typedef typename KernSubTableHeader::Types Types; + typedef typename Types::HBUINT HBUINT; + + int get_kerning (hb_codepoint_t left, hb_codepoint_t right, + hb_aat_apply_context_t *c) const + { + unsigned int num_glyphs = c->sanitizer.get_num_glyphs (); + unsigned int l = (this+leftClassTable).get_class (left, num_glyphs, 0); + unsigned int r = (this+rightClassTable).get_class (right, num_glyphs, 0); + + const UnsizedArrayOf &arrayZ = this+array; + unsigned int kern_idx = l + r; + kern_idx = Types::offsetToIndex (kern_idx, this, arrayZ.arrayZ); + const FWORD *v = &arrayZ[kern_idx]; + if (unlikely (!v->sanitize (&c->sanitizer))) return 0; + + return kerxTupleKern (*v, header.tuple_count (), this, c); + } + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + if (!c->plan->requested_kerning) + return false; + + if (header.coverage & header.Backwards) + return false; + + accelerator_t accel (*this, c); + hb_kern_machine_t machine (accel, header.coverage & header.CrossStream); + machine.kern (c->font, c->buffer, c->plan->kern_mask); + + return_trace (true); + } + + struct accelerator_t + { + const KerxSubTableFormat2 &table; + hb_aat_apply_context_t *c; + + accelerator_t (const KerxSubTableFormat2 &table_, + hb_aat_apply_context_t *c_) : + table (table_), c (c_) {} + + int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const + { return table.get_kerning (left, right, c); } + }; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + leftClassTable.sanitize (c, this) && + rightClassTable.sanitize (c, this) && + c->check_range (this, array))); + } + + protected: + KernSubTableHeader header; + HBUINT rowWidth; /* The width, in bytes, of a row in the table. */ + NNOffsetTo + leftClassTable; /* Offset from beginning of this subtable to + * left-hand class table. */ + NNOffsetTo + rightClassTable;/* Offset from beginning of this subtable to + * right-hand class table. */ + NNOffsetTo, HBUINT> + array; /* Offset from beginning of this subtable to + * the start of the kerning array. */ + public: + DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 4 * sizeof (HBUINT)); +}; + +template +struct KerxSubTableFormat4 +{ + typedef ExtendedTypes Types; + + struct EntryData + { + HBUINT16 ankrActionIndex;/* Either 0xFFFF (for no action) or the index of + * the action to perform. */ + public: + DEFINE_SIZE_STATIC (2); + }; + + struct driver_context_t + { + static constexpr bool in_place = true; + enum Flags + { + Mark = 0x8000, /* If set, remember this glyph as the marked glyph. */ + DontAdvance = 0x4000, /* If set, don't advance to the next glyph before + * going to the new state. */ + Reserved = 0x3FFF, /* Not used; set to 0. */ + }; + + enum SubTableFlags + { + ActionType = 0xC0000000, /* A two-bit field containing the action type. */ + Unused = 0x3F000000, /* Unused - must be zero. */ + Offset = 0x00FFFFFF, /* Masks the offset in bytes from the beginning + * of the subtable to the beginning of the control + * point table. */ + }; + + driver_context_t (const KerxSubTableFormat4 *table, + hb_aat_apply_context_t *c_) : + c (c_), + action_type ((table->flags & ActionType) >> 30), + ankrData ((HBUINT16 *) ((const char *) &table->machine + (table->flags & Offset))), + mark_set (false), + mark (0) {} + + bool is_actionable (StateTableDriver *driver HB_UNUSED, + const Entry &entry) + { return entry.data.ankrActionIndex != 0xFFFF; } + void transition (StateTableDriver *driver, + const Entry &entry) + { + hb_buffer_t *buffer = driver->buffer; + + if (mark_set && entry.data.ankrActionIndex != 0xFFFF && buffer->idx < buffer->len) + { + hb_glyph_position_t &o = buffer->cur_pos(); + switch (action_type) + { + case 0: /* Control Point Actions.*/ + { + /* Indexed into glyph outline. */ + /* Each action (record in ankrData) contains two 16-bit fields, so we must + double the ankrActionIndex to get the correct offset here. */ + const HBUINT16 *data = &ankrData[entry.data.ankrActionIndex * 2]; + if (!c->sanitizer.check_array (data, 2)) return; + unsigned int markControlPoint = *data++; + unsigned int currControlPoint = *data++; + hb_position_t markX = 0; + hb_position_t markY = 0; + hb_position_t currX = 0; + hb_position_t currY = 0; + if (!c->font->get_glyph_contour_point_for_origin (c->buffer->info[mark].codepoint, + markControlPoint, + HB_DIRECTION_LTR /*XXX*/, + &markX, &markY) || + !c->font->get_glyph_contour_point_for_origin (c->buffer->cur ().codepoint, + currControlPoint, + HB_DIRECTION_LTR /*XXX*/, + &currX, &currY)) + return; + + o.x_offset = markX - currX; + o.y_offset = markY - currY; + } + break; + + case 1: /* Anchor Point Actions. */ + { + /* Indexed into 'ankr' table. */ + /* Each action (record in ankrData) contains two 16-bit fields, so we must + double the ankrActionIndex to get the correct offset here. */ + const HBUINT16 *data = &ankrData[entry.data.ankrActionIndex * 2]; + if (!c->sanitizer.check_array (data, 2)) return; + unsigned int markAnchorPoint = *data++; + unsigned int currAnchorPoint = *data++; + const Anchor &markAnchor = c->ankr_table->get_anchor (c->buffer->info[mark].codepoint, + markAnchorPoint, + c->sanitizer.get_num_glyphs ()); + const Anchor &currAnchor = c->ankr_table->get_anchor (c->buffer->cur ().codepoint, + currAnchorPoint, + c->sanitizer.get_num_glyphs ()); + + o.x_offset = c->font->em_scale_x (markAnchor.xCoordinate) - c->font->em_scale_x (currAnchor.xCoordinate); + o.y_offset = c->font->em_scale_y (markAnchor.yCoordinate) - c->font->em_scale_y (currAnchor.yCoordinate); + } + break; + + case 2: /* Control Point Coordinate Actions. */ + { + /* Each action contains four 16-bit fields, so we multiply the ankrActionIndex + by 4 to get the correct offset for the given action. */ + const FWORD *data = (const FWORD *) &ankrData[entry.data.ankrActionIndex * 4]; + if (!c->sanitizer.check_array (data, 4)) return; + int markX = *data++; + int markY = *data++; + int currX = *data++; + int currY = *data++; + + o.x_offset = c->font->em_scale_x (markX) - c->font->em_scale_x (currX); + o.y_offset = c->font->em_scale_y (markY) - c->font->em_scale_y (currY); + } + break; + } + o.attach_type() = OT::Layout::GPOS_impl::ATTACH_TYPE_MARK; + o.attach_chain() = (int) mark - (int) buffer->idx; + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; + } + + if (entry.flags & Mark) + { + mark_set = true; + mark = buffer->idx; + } + } + + private: + hb_aat_apply_context_t *c; + unsigned int action_type; + const HBUINT16 *ankrData; + bool mark_set; + unsigned int mark; + }; + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + driver_context_t dc (this, c); + + StateTableDriver driver (machine, c->buffer, c->font->face); + driver.drive (&dc, c); + + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + /* The rest of array sanitizations are done at run-time. */ + return_trace (likely (c->check_struct (this) && + machine.sanitize (c))); + } + + protected: + KernSubTableHeader header; + StateTable machine; + HBUINT32 flags; + public: + DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 20); +}; + +template +struct KerxSubTableFormat6 +{ + enum Flags + { + ValuesAreLong = 0x00000001, + }; + + bool is_long () const { return flags & ValuesAreLong; } + + int get_kerning (hb_codepoint_t left, hb_codepoint_t right, + hb_aat_apply_context_t *c) const + { + unsigned int num_glyphs = c->sanitizer.get_num_glyphs (); + if (is_long ()) + { + const typename U::Long &t = u.l; + unsigned int l = (this+t.rowIndexTable).get_value_or_null (left, num_glyphs); + unsigned int r = (this+t.columnIndexTable).get_value_or_null (right, num_glyphs); + unsigned int offset = l + r; + if (unlikely (offset < l)) return 0; /* Addition overflow. */ + if (unlikely (hb_unsigned_mul_overflows (offset, sizeof (FWORD32)))) return 0; + const FWORD32 *v = &StructAtOffset (&(this+t.array), offset * sizeof (FWORD32)); + if (unlikely (!v->sanitize (&c->sanitizer))) return 0; + return kerxTupleKern (*v, header.tuple_count (), &(this+vector), c); + } + else + { + const typename U::Short &t = u.s; + unsigned int l = (this+t.rowIndexTable).get_value_or_null (left, num_glyphs); + unsigned int r = (this+t.columnIndexTable).get_value_or_null (right, num_glyphs); + unsigned int offset = l + r; + const FWORD *v = &StructAtOffset (&(this+t.array), offset * sizeof (FWORD)); + if (unlikely (!v->sanitize (&c->sanitizer))) return 0; + return kerxTupleKern (*v, header.tuple_count (), &(this+vector), c); + } + } + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + if (!c->plan->requested_kerning) + return false; + + if (header.coverage & header.Backwards) + return false; + + accelerator_t accel (*this, c); + hb_kern_machine_t machine (accel, header.coverage & header.CrossStream); + machine.kern (c->font, c->buffer, c->plan->kern_mask); + + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + (is_long () ? + ( + u.l.rowIndexTable.sanitize (c, this) && + u.l.columnIndexTable.sanitize (c, this) && + c->check_range (this, u.l.array) + ) : ( + u.s.rowIndexTable.sanitize (c, this) && + u.s.columnIndexTable.sanitize (c, this) && + c->check_range (this, u.s.array) + )) && + (header.tuple_count () == 0 || + c->check_range (this, vector)))); + } + + struct accelerator_t + { + const KerxSubTableFormat6 &table; + hb_aat_apply_context_t *c; + + accelerator_t (const KerxSubTableFormat6 &table_, + hb_aat_apply_context_t *c_) : + table (table_), c (c_) {} + + int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const + { return table.get_kerning (left, right, c); } + }; + + protected: + KernSubTableHeader header; + HBUINT32 flags; + HBUINT16 rowCount; + HBUINT16 columnCount; + union U + { + struct Long + { + NNOffset32To> rowIndexTable; + NNOffset32To> columnIndexTable; + NNOffset32To> array; + } l; + struct Short + { + NNOffset32To> rowIndexTable; + NNOffset32To> columnIndexTable; + NNOffset32To> array; + } s; + } u; + NNOffset32To> vector; + public: + DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 24); +}; + + +struct KerxSubTableHeader +{ + typedef ExtendedTypes Types; + + unsigned tuple_count () const { return tupleCount; } + bool is_horizontal () const { return !(coverage & Vertical); } + + enum Coverage + { + Vertical = 0x80000000u, /* Set if table has vertical kerning values. */ + CrossStream = 0x40000000u, /* Set if table has cross-stream kerning values. */ + Variation = 0x20000000u, /* Set if table has variation kerning values. */ + Backwards = 0x10000000u, /* If clear, process the glyphs forwards, that + * is, from first to last in the glyph stream. + * If we, process them from last to first. + * This flag only applies to state-table based + * 'kerx' subtables (types 1 and 4). */ + Reserved = 0x0FFFFF00u, /* Reserved, set to zero. */ + SubtableType= 0x000000FFu, /* Subtable type. */ + }; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + HBUINT32 length; + HBUINT32 coverage; + HBUINT32 tupleCount; + public: + DEFINE_SIZE_STATIC (12); +}; + +struct KerxSubTable +{ + friend struct kerx; + + unsigned int get_size () const { return u.header.length; } + unsigned int get_type () const { return u.header.coverage & u.header.SubtableType; } + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + unsigned int subtable_type = get_type (); + TRACE_DISPATCH (this, subtable_type); + switch (subtable_type) { + case 0: return_trace (c->dispatch (u.format0, std::forward (ds)...)); + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); + case 4: return_trace (c->dispatch (u.format4, std::forward (ds)...)); + case 6: return_trace (c->dispatch (u.format6, std::forward (ds)...)); + default: return_trace (c->default_return_value ()); + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.header.sanitize (c) || + u.header.length <= u.header.static_size || + !c->check_range (this, u.header.length)) + return_trace (false); + + return_trace (dispatch (c)); + } + + public: + union { + KerxSubTableHeader header; + KerxSubTableFormat0 format0; + KerxSubTableFormat1 format1; + KerxSubTableFormat2 format2; + KerxSubTableFormat4 format4; + KerxSubTableFormat6 format6; + } u; + public: + DEFINE_SIZE_MIN (12); +}; + + +/* + * The 'kerx' Table + */ + +template +struct KerxTable +{ + /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */ + const T* thiz () const { return static_cast (this); } + + bool has_state_machine () const + { + typedef typename T::SubTable SubTable; + + const SubTable *st = &thiz()->firstSubTable; + unsigned int count = thiz()->tableCount; + for (unsigned int i = 0; i < count; i++) + { + if (st->get_type () == 1) + return true; + st = &StructAfter (*st); + } + return false; + } + + bool has_cross_stream () const + { + typedef typename T::SubTable SubTable; + + const SubTable *st = &thiz()->firstSubTable; + unsigned int count = thiz()->tableCount; + for (unsigned int i = 0; i < count; i++) + { + if (st->u.header.coverage & st->u.header.CrossStream) + return true; + st = &StructAfter (*st); + } + return false; + } + + int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right) const + { + typedef typename T::SubTable SubTable; + + int v = 0; + const SubTable *st = &thiz()->firstSubTable; + unsigned int count = thiz()->tableCount; + for (unsigned int i = 0; i < count; i++) + { + if ((st->u.header.coverage & (st->u.header.Variation | st->u.header.CrossStream)) || + !st->u.header.is_horizontal ()) + continue; + v += st->get_kerning (left, right); + st = &StructAfter (*st); + } + return v; + } + + bool apply (AAT::hb_aat_apply_context_t *c) const + { + c->buffer->unsafe_to_concat (); + + typedef typename T::SubTable SubTable; + + bool ret = false; + bool seenCrossStream = false; + c->set_lookup_index (0); + const SubTable *st = &thiz()->firstSubTable; + unsigned int count = thiz()->tableCount; + for (unsigned int i = 0; i < count; i++) + { + bool reverse; + + if (!T::Types::extended && (st->u.header.coverage & st->u.header.Variation)) + goto skip; + + if (HB_DIRECTION_IS_HORIZONTAL (c->buffer->props.direction) != st->u.header.is_horizontal ()) + goto skip; + + reverse = bool (st->u.header.coverage & st->u.header.Backwards) != + HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction); + + if (!c->buffer->message (c->font, "start subtable %u", c->lookup_index)) + goto skip; + + if (!seenCrossStream && + (st->u.header.coverage & st->u.header.CrossStream)) + { + /* Attach all glyphs into a chain. */ + seenCrossStream = true; + hb_glyph_position_t *pos = c->buffer->pos; + unsigned int count = c->buffer->len; + for (unsigned int i = 0; i < count; i++) + { + pos[i].attach_type() = OT::Layout::GPOS_impl::ATTACH_TYPE_CURSIVE; + pos[i].attach_chain() = HB_DIRECTION_IS_FORWARD (c->buffer->props.direction) ? -1 : +1; + /* We intentionally don't set HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT, + * since there needs to be a non-zero attachment for post-positioning to + * be needed. */ + } + } + + if (reverse) + c->buffer->reverse (); + + { + /* See comment in sanitize() for conditional here. */ + hb_sanitize_with_object_t with (&c->sanitizer, i < count - 1 ? st : (const SubTable *) nullptr); + ret |= st->dispatch (c); + } + + if (reverse) + c->buffer->reverse (); + + (void) c->buffer->message (c->font, "end subtable %u", c->lookup_index); + + skip: + st = &StructAfter (*st); + c->set_lookup_index (c->lookup_index + 1); + } + + return ret; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!thiz()->version.sanitize (c) || + (unsigned) thiz()->version < (unsigned) T::minVersion || + !thiz()->tableCount.sanitize (c))) + return_trace (false); + + typedef typename T::SubTable SubTable; + + const SubTable *st = &thiz()->firstSubTable; + unsigned int count = thiz()->tableCount; + for (unsigned int i = 0; i < count; i++) + { + if (unlikely (!st->u.header.sanitize (c))) + return_trace (false); + /* OpenType kern table has 2-byte subtable lengths. That's limiting. + * MS implementation also only supports one subtable, of format 0, + * anyway. Certain versions of some fonts, like Calibry, contain + * kern subtable that exceeds 64kb. Looks like, the subtable length + * is simply ignored. Which makes sense. It's only needed if you + * have multiple subtables. To handle such fonts, we just ignore + * the length for the last subtable. */ + hb_sanitize_with_object_t with (c, i < count - 1 ? st : (const SubTable *) nullptr); + + if (unlikely (!st->sanitize (c))) + return_trace (false); + + st = &StructAfter (*st); + } + + return_trace (true); + } +}; + +struct kerx : KerxTable +{ + friend struct KerxTable; + + static constexpr hb_tag_t tableTag = HB_AAT_TAG_kerx; + static constexpr unsigned minVersion = 2u; + + typedef KerxSubTableHeader SubTableHeader; + typedef SubTableHeader::Types Types; + typedef KerxSubTable SubTable; + + bool has_data () const { return version; } + + protected: + HBUINT16 version; /* The version number of the extended kerning table + * (currently 2, 3, or 4). */ + HBUINT16 unused; /* Set to 0. */ + HBUINT32 tableCount; /* The number of subtables included in the extended kerning + * table. */ + SubTable firstSubTable; /* Subtables. */ +/*subtableGlyphCoverageArray*/ /* Only if version >= 3. We don't use. */ + + public: + DEFINE_SIZE_MIN (8); +}; + + +} /* namespace AAT */ + + +#endif /* HB_AAT_LAYOUT_KERX_TABLE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-morx-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-morx-table.hh new file mode 100644 index 00000000000..f41ecc197fb --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-morx-table.hh @@ -0,0 +1,1210 @@ +/* + * Copyright © 2017 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): Behdad Esfahbod + */ + +#ifndef HB_AAT_LAYOUT_MORX_TABLE_HH +#define HB_AAT_LAYOUT_MORX_TABLE_HH + +#include "hb-open-type.hh" +#include "hb-aat-layout-common.hh" +#include "hb-ot-layout-common.hh" +#include "hb-ot-layout-gdef-table.hh" +#include "hb-aat-map.hh" + +/* + * morx -- Extended Glyph Metamorphosis + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6morx.html + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6mort.html + */ +#define HB_AAT_TAG_morx HB_TAG('m','o','r','x') +#define HB_AAT_TAG_mort HB_TAG('m','o','r','t') + + +namespace AAT { + +using namespace OT; + +template +struct RearrangementSubtable +{ + typedef typename Types::HBUINT HBUINT; + + typedef void EntryData; + + struct driver_context_t + { + static constexpr bool in_place = true; + enum Flags + { + MarkFirst = 0x8000, /* If set, make the current glyph the first + * glyph to be rearranged. */ + DontAdvance = 0x4000, /* If set, don't advance to the next glyph + * before going to the new state. This means + * that the glyph index doesn't change, even + * if the glyph at that index has changed. */ + MarkLast = 0x2000, /* If set, make the current glyph the last + * glyph to be rearranged. */ + Reserved = 0x1FF0, /* These bits are reserved and should be set to 0. */ + Verb = 0x000F, /* The type of rearrangement specified. */ + }; + + driver_context_t (const RearrangementSubtable *table HB_UNUSED) : + ret (false), + start (0), end (0) {} + + bool is_actionable (StateTableDriver *driver HB_UNUSED, + const Entry &entry) + { + return (entry.flags & Verb) && start < end; + } + void transition (StateTableDriver *driver, + const Entry &entry) + { + hb_buffer_t *buffer = driver->buffer; + unsigned int flags = entry.flags; + + if (flags & MarkFirst) + start = buffer->idx; + + if (flags & MarkLast) + end = hb_min (buffer->idx + 1, buffer->len); + + if ((flags & Verb) && start < end) + { + /* The following map has two nibbles, for start-side + * and end-side. Values of 0,1,2 mean move that many + * to the other side. Value of 3 means move 2 and + * flip them. */ + const unsigned char map[16] = + { + 0x00, /* 0 no change */ + 0x10, /* 1 Ax => xA */ + 0x01, /* 2 xD => Dx */ + 0x11, /* 3 AxD => DxA */ + 0x20, /* 4 ABx => xAB */ + 0x30, /* 5 ABx => xBA */ + 0x02, /* 6 xCD => CDx */ + 0x03, /* 7 xCD => DCx */ + 0x12, /* 8 AxCD => CDxA */ + 0x13, /* 9 AxCD => DCxA */ + 0x21, /* 10 ABxD => DxAB */ + 0x31, /* 11 ABxD => DxBA */ + 0x22, /* 12 ABxCD => CDxAB */ + 0x32, /* 13 ABxCD => CDxBA */ + 0x23, /* 14 ABxCD => DCxAB */ + 0x33, /* 15 ABxCD => DCxBA */ + }; + + unsigned int m = map[flags & Verb]; + unsigned int l = hb_min (2u, m >> 4); + unsigned int r = hb_min (2u, m & 0x0F); + bool reverse_l = 3 == (m >> 4); + bool reverse_r = 3 == (m & 0x0F); + + if (end - start >= l + r && end-start <= HB_MAX_CONTEXT_LENGTH) + { + buffer->merge_clusters (start, hb_min (buffer->idx + 1, buffer->len)); + buffer->merge_clusters (start, end); + + hb_glyph_info_t *info = buffer->info; + hb_glyph_info_t buf[4]; + + hb_memcpy (buf, info + start, l * sizeof (buf[0])); + hb_memcpy (buf + 2, info + end - r, r * sizeof (buf[0])); + + if (l != r) + memmove (info + start + r, info + start + l, (end - start - l - r) * sizeof (buf[0])); + + hb_memcpy (info + start, buf + 2, r * sizeof (buf[0])); + hb_memcpy (info + end - l, buf, l * sizeof (buf[0])); + if (reverse_l) + { + buf[0] = info[end - 1]; + info[end - 1] = info[end - 2]; + info[end - 2] = buf[0]; + } + if (reverse_r) + { + buf[0] = info[start]; + info[start] = info[start + 1]; + info[start + 1] = buf[0]; + } + } + } + } + + public: + bool ret; + private: + unsigned int start; + unsigned int end; + }; + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + driver_context_t dc (this); + + StateTableDriver driver (machine, c->buffer, c->face); + driver.drive (&dc, c); + + return_trace (dc.ret); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (machine.sanitize (c)); + } + + protected: + StateTable machine; + public: + DEFINE_SIZE_STATIC (16); +}; + +template +struct ContextualSubtable +{ + typedef typename Types::HBUINT HBUINT; + + struct EntryData + { + HBUINT16 markIndex; /* Index of the substitution table for the + * marked glyph (use 0xFFFF for none). */ + HBUINT16 currentIndex; /* Index of the substitution table for the + * current glyph (use 0xFFFF for none). */ + public: + DEFINE_SIZE_STATIC (4); + }; + + struct driver_context_t + { + static constexpr bool in_place = true; + enum Flags + { + SetMark = 0x8000, /* If set, make the current glyph the marked glyph. */ + DontAdvance = 0x4000, /* If set, don't advance to the next glyph before + * going to the new state. */ + Reserved = 0x3FFF, /* These bits are reserved and should be set to 0. */ + }; + + driver_context_t (const ContextualSubtable *table_, + hb_aat_apply_context_t *c_) : + ret (false), + c (c_), + gdef (*c->gdef_table), + mark_set (false), + has_glyph_classes (gdef.has_glyph_classes ()), + mark (0), + table (table_), + subs (table+table->substitutionTables) {} + + bool is_actionable (StateTableDriver *driver, + const Entry &entry) + { + hb_buffer_t *buffer = driver->buffer; + + if (buffer->idx == buffer->len && !mark_set) + return false; + + return entry.data.markIndex != 0xFFFF || entry.data.currentIndex != 0xFFFF; + } + void transition (StateTableDriver *driver, + const Entry &entry) + { + hb_buffer_t *buffer = driver->buffer; + + /* Looks like CoreText applies neither mark nor current substitution for + * end-of-text if mark was not explicitly set. */ + if (buffer->idx == buffer->len && !mark_set) + return; + + const HBGlyphID16 *replacement; + + replacement = nullptr; + if (Types::extended) + { + if (entry.data.markIndex != 0xFFFF) + { + const Lookup &lookup = subs[entry.data.markIndex]; + replacement = lookup.get_value (buffer->info[mark].codepoint, driver->num_glyphs); + } + } + else + { + unsigned int offset = entry.data.markIndex + buffer->info[mark].codepoint; + const UnsizedArrayOf &subs_old = (const UnsizedArrayOf &) subs; + replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)]; + if (!replacement->sanitize (&c->sanitizer) || !*replacement) + replacement = nullptr; + } + if (replacement) + { + buffer->unsafe_to_break (mark, hb_min (buffer->idx + 1, buffer->len)); + buffer->info[mark].codepoint = *replacement; + if (has_glyph_classes) + _hb_glyph_info_set_glyph_props (&buffer->info[mark], + gdef.get_glyph_props (*replacement)); + ret = true; + } + + replacement = nullptr; + unsigned int idx = hb_min (buffer->idx, buffer->len - 1); + if (Types::extended) + { + if (entry.data.currentIndex != 0xFFFF) + { + const Lookup &lookup = subs[entry.data.currentIndex]; + replacement = lookup.get_value (buffer->info[idx].codepoint, driver->num_glyphs); + } + } + else + { + unsigned int offset = entry.data.currentIndex + buffer->info[idx].codepoint; + const UnsizedArrayOf &subs_old = (const UnsizedArrayOf &) subs; + replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)]; + if (!replacement->sanitize (&c->sanitizer) || !*replacement) + replacement = nullptr; + } + if (replacement) + { + buffer->info[idx].codepoint = *replacement; + if (has_glyph_classes) + _hb_glyph_info_set_glyph_props (&buffer->info[idx], + gdef.get_glyph_props (*replacement)); + ret = true; + } + + if (entry.flags & SetMark) + { + mark_set = true; + mark = buffer->idx; + } + } + + public: + bool ret; + private: + hb_aat_apply_context_t *c; + const OT::GDEF &gdef; + bool mark_set; + bool has_glyph_classes; + unsigned int mark; + const ContextualSubtable *table; + const UnsizedListOfOffset16To, HBUINT, false> &subs; + }; + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + driver_context_t dc (this, c); + + StateTableDriver driver (machine, c->buffer, c->face); + driver.drive (&dc, c); + + return_trace (dc.ret); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + + unsigned int num_entries = 0; + if (unlikely (!machine.sanitize (c, &num_entries))) return_trace (false); + + if (!Types::extended) + return_trace (substitutionTables.sanitize (c, this, 0)); + + unsigned int num_lookups = 0; + + const Entry *entries = machine.get_entries (); + for (unsigned int i = 0; i < num_entries; i++) + { + const EntryData &data = entries[i].data; + + if (data.markIndex != 0xFFFF) + num_lookups = hb_max (num_lookups, 1u + data.markIndex); + if (data.currentIndex != 0xFFFF) + num_lookups = hb_max (num_lookups, 1u + data.currentIndex); + } + + return_trace (substitutionTables.sanitize (c, this, num_lookups)); + } + + protected: + StateTable + machine; + NNOffsetTo, HBUINT, false>, HBUINT> + substitutionTables; + public: + DEFINE_SIZE_STATIC (20); +}; + + +template +struct LigatureEntry; + +template <> +struct LigatureEntry +{ + enum Flags + { + SetComponent = 0x8000, /* Push this glyph onto the component stack for + * eventual processing. */ + DontAdvance = 0x4000, /* Leave the glyph pointer at this glyph for the + next iteration. */ + PerformAction = 0x2000, /* Use the ligActionIndex to process a ligature + * group. */ + Reserved = 0x1FFF, /* These bits are reserved and should be set to 0. */ + }; + + struct EntryData + { + HBUINT16 ligActionIndex; /* Index to the first ligActionTable entry + * for processing this group, if indicated + * by the flags. */ + public: + DEFINE_SIZE_STATIC (2); + }; + + static bool performAction (const Entry &entry) + { return entry.flags & PerformAction; } + + static unsigned int ligActionIndex (const Entry &entry) + { return entry.data.ligActionIndex; } +}; +template <> +struct LigatureEntry +{ + enum Flags + { + SetComponent = 0x8000, /* Push this glyph onto the component stack for + * eventual processing. */ + DontAdvance = 0x4000, /* Leave the glyph pointer at this glyph for the + next iteration. */ + Offset = 0x3FFF, /* Byte offset from beginning of subtable to the + * ligature action list. This value must be a + * multiple of 4. */ + }; + + typedef void EntryData; + + static bool performAction (const Entry &entry) + { return entry.flags & Offset; } + + static unsigned int ligActionIndex (const Entry &entry) + { return entry.flags & Offset; } +}; + + +template +struct LigatureSubtable +{ + typedef typename Types::HBUINT HBUINT; + + typedef LigatureEntry LigatureEntryT; + typedef typename LigatureEntryT::EntryData EntryData; + + struct driver_context_t + { + static constexpr bool in_place = false; + enum + { + DontAdvance = LigatureEntryT::DontAdvance, + }; + enum LigActionFlags + { + LigActionLast = 0x80000000, /* This is the last action in the list. This also + * implies storage. */ + LigActionStore = 0x40000000, /* Store the ligature at the current cumulated index + * in the ligature table in place of the marked + * (i.e. currently-popped) glyph. */ + LigActionOffset = 0x3FFFFFFF, /* A 30-bit value which is sign-extended to 32-bits + * and added to the glyph ID, resulting in an index + * into the component table. */ + }; + + driver_context_t (const LigatureSubtable *table_, + hb_aat_apply_context_t *c_) : + ret (false), + c (c_), + table (table_), + ligAction (table+table->ligAction), + component (table+table->component), + ligature (table+table->ligature), + match_length (0) {} + + bool is_actionable (StateTableDriver *driver HB_UNUSED, + const Entry &entry) + { + return LigatureEntryT::performAction (entry); + } + void transition (StateTableDriver *driver, + const Entry &entry) + { + hb_buffer_t *buffer = driver->buffer; + + DEBUG_MSG (APPLY, nullptr, "Ligature transition at %u", buffer->idx); + if (entry.flags & LigatureEntryT::SetComponent) + { + /* Never mark same index twice, in case DontAdvance was used... */ + if (match_length && match_positions[(match_length - 1u) % ARRAY_LENGTH (match_positions)] == buffer->out_len) + match_length--; + + match_positions[match_length++ % ARRAY_LENGTH (match_positions)] = buffer->out_len; + DEBUG_MSG (APPLY, nullptr, "Set component at %u", buffer->out_len); + } + + if (LigatureEntryT::performAction (entry)) + { + DEBUG_MSG (APPLY, nullptr, "Perform action with %u", match_length); + unsigned int end = buffer->out_len; + + if (unlikely (!match_length)) + return; + + if (buffer->idx >= buffer->len) + return; /* TODO Work on previous instead? */ + + unsigned int cursor = match_length; + + unsigned int action_idx = LigatureEntryT::ligActionIndex (entry); + action_idx = Types::offsetToIndex (action_idx, table, ligAction.arrayZ); + const HBUINT32 *actionData = &ligAction[action_idx]; + + unsigned int ligature_idx = 0; + unsigned int action; + do + { + if (unlikely (!cursor)) + { + /* Stack underflow. Clear the stack. */ + DEBUG_MSG (APPLY, nullptr, "Stack underflow"); + match_length = 0; + break; + } + + DEBUG_MSG (APPLY, nullptr, "Moving to stack position %u", cursor - 1); + if (unlikely (!buffer->move_to (match_positions[--cursor % ARRAY_LENGTH (match_positions)]))) return; + + if (unlikely (!actionData->sanitize (&c->sanitizer))) break; + action = *actionData; + + uint32_t uoffset = action & LigActionOffset; + if (uoffset & 0x20000000) + uoffset |= 0xC0000000; /* Sign-extend. */ + int32_t offset = (int32_t) uoffset; + unsigned int component_idx = buffer->cur().codepoint + offset; + component_idx = Types::wordOffsetToIndex (component_idx, table, component.arrayZ); + const HBUINT16 &componentData = component[component_idx]; + if (unlikely (!componentData.sanitize (&c->sanitizer))) break; + ligature_idx += componentData; + + DEBUG_MSG (APPLY, nullptr, "Action store %d last %d", + bool (action & LigActionStore), + bool (action & LigActionLast)); + if (action & (LigActionStore | LigActionLast)) + { + ligature_idx = Types::offsetToIndex (ligature_idx, table, ligature.arrayZ); + const HBGlyphID16 &ligatureData = ligature[ligature_idx]; + if (unlikely (!ligatureData.sanitize (&c->sanitizer))) break; + hb_codepoint_t lig = ligatureData; + + DEBUG_MSG (APPLY, nullptr, "Produced ligature %u", lig); + if (unlikely (!buffer->replace_glyph (lig))) return; + + unsigned int lig_end = match_positions[(match_length - 1u) % ARRAY_LENGTH (match_positions)] + 1u; + /* Now go and delete all subsequent components. */ + while (match_length - 1u > cursor) + { + DEBUG_MSG (APPLY, nullptr, "Skipping ligature component"); + if (unlikely (!buffer->move_to (match_positions[--match_length % ARRAY_LENGTH (match_positions)]))) return; + if (unlikely (!buffer->replace_glyph (DELETED_GLYPH))) return; + } + + if (unlikely (!buffer->move_to (lig_end))) return; + buffer->merge_out_clusters (match_positions[cursor % ARRAY_LENGTH (match_positions)], buffer->out_len); + } + + actionData++; + } + while (!(action & LigActionLast)); + if (unlikely (!buffer->move_to (end))) return; + } + } + + public: + bool ret; + private: + hb_aat_apply_context_t *c; + const LigatureSubtable *table; + const UnsizedArrayOf &ligAction; + const UnsizedArrayOf &component; + const UnsizedArrayOf &ligature; + unsigned int match_length; + unsigned int match_positions[HB_MAX_CONTEXT_LENGTH]; + }; + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + driver_context_t dc (this, c); + + StateTableDriver driver (machine, c->buffer, c->face); + driver.drive (&dc, c); + + return_trace (dc.ret); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + /* The rest of array sanitizations are done at run-time. */ + return_trace (c->check_struct (this) && machine.sanitize (c) && + ligAction && component && ligature); + } + + protected: + StateTable + machine; + NNOffsetTo, HBUINT> + ligAction; /* Offset to the ligature action table. */ + NNOffsetTo, HBUINT> + component; /* Offset to the component table. */ + NNOffsetTo, HBUINT> + ligature; /* Offset to the actual ligature lists. */ + public: + DEFINE_SIZE_STATIC (28); +}; + +template +struct NoncontextualSubtable +{ + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + const OT::GDEF &gdef (*c->gdef_table); + bool has_glyph_classes = gdef.has_glyph_classes (); + + bool ret = false; + unsigned int num_glyphs = c->face->get_num_glyphs (); + + hb_glyph_info_t *info = c->buffer->info; + unsigned int count = c->buffer->len; + // If there's only one range, we already checked the flag. + auto *last_range = c->range_flags && (c->range_flags->length > 1) ? &(*c->range_flags)[0] : nullptr; + for (unsigned int i = 0; i < count; i++) + { + /* This block copied from StateTableDriver::drive. Keep in sync. */ + if (last_range) + { + auto *range = last_range; + { + unsigned cluster = info[i].cluster; + while (cluster < range->cluster_first) + range--; + while (cluster > range->cluster_last) + range++; + + last_range = range; + } + if (!(range->flags & c->subtable_flags)) + continue; + } + + const HBGlyphID16 *replacement = substitute.get_value (info[i].codepoint, num_glyphs); + if (replacement) + { + info[i].codepoint = *replacement; + if (has_glyph_classes) + _hb_glyph_info_set_glyph_props (&info[i], + gdef.get_glyph_props (*replacement)); + ret = true; + } + } + + return_trace (ret); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (substitute.sanitize (c)); + } + + protected: + Lookup substitute; + public: + DEFINE_SIZE_MIN (2); +}; + +template +struct InsertionSubtable +{ + typedef typename Types::HBUINT HBUINT; + + struct EntryData + { + HBUINT16 currentInsertIndex; /* Zero-based index into the insertion glyph table. + * The number of glyphs to be inserted is contained + * in the currentInsertCount field in the flags. + * A value of 0xFFFF indicates no insertion is to + * be done. */ + HBUINT16 markedInsertIndex; /* Zero-based index into the insertion glyph table. + * The number of glyphs to be inserted is contained + * in the markedInsertCount field in the flags. + * A value of 0xFFFF indicates no insertion is to + * be done. */ + public: + DEFINE_SIZE_STATIC (4); + }; + + struct driver_context_t + { + static constexpr bool in_place = false; + enum Flags + { + SetMark = 0x8000, /* If set, mark the current glyph. */ + DontAdvance = 0x4000, /* If set, don't advance to the next glyph 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. */ + CurrentIsKashidaLike= 0x2000, /* If set, and the currentInsertList is nonzero, + * then the specified glyph list will be inserted + * as a kashida-like insertion, either before or + * after the current glyph (depending on the state + * of the currentInsertBefore flag). If clear, and + * the currentInsertList is nonzero, then the + * specified glyph list will be inserted as a + * split-vowel-like insertion, either before or + * after the current glyph (depending on the state + * of the currentInsertBefore flag). */ + MarkedIsKashidaLike= 0x1000, /* If set, and the markedInsertList is nonzero, + * then the specified glyph list will be inserted + * as a kashida-like insertion, either before or + * after the marked glyph (depending on the state + * of the markedInsertBefore flag). If clear, and + * the markedInsertList is nonzero, then the + * specified glyph list will be inserted as a + * split-vowel-like insertion, either before or + * after the marked glyph (depending on the state + * of the markedInsertBefore flag). */ + CurrentInsertBefore= 0x0800, /* If set, specifies that insertions are to be made + * to the left of the current glyph. If clear, + * they're made to the right of the current glyph. */ + MarkedInsertBefore= 0x0400, /* If set, specifies that insertions are to be + * made to the left of the marked glyph. If clear, + * they're made to the right of the marked glyph. */ + CurrentInsertCount= 0x3E0, /* This 5-bit field is treated as a count of the + * number of glyphs to insert at the current + * position. Since zero means no insertions, the + * largest number of insertions at any given + * current location is 31 glyphs. */ + MarkedInsertCount= 0x001F, /* This 5-bit field is treated as a count of the + * number of glyphs to insert at the marked + * position. Since zero means no insertions, the + * largest number of insertions at any given + * marked location is 31 glyphs. */ + }; + + driver_context_t (const InsertionSubtable *table, + hb_aat_apply_context_t *c_) : + ret (false), + c (c_), + mark (0), + insertionAction (table+table->insertionAction) {} + + bool is_actionable (StateTableDriver *driver HB_UNUSED, + const Entry &entry) + { + return (entry.flags & (CurrentInsertCount | MarkedInsertCount)) && + (entry.data.currentInsertIndex != 0xFFFF ||entry.data.markedInsertIndex != 0xFFFF); + } + void transition (StateTableDriver *driver, + const Entry &entry) + { + hb_buffer_t *buffer = driver->buffer; + unsigned int flags = entry.flags; + + unsigned mark_loc = buffer->out_len; + + if (entry.data.markedInsertIndex != 0xFFFF) + { + unsigned int count = (flags & MarkedInsertCount); + if (unlikely ((buffer->max_ops -= count) <= 0)) return; + unsigned int start = entry.data.markedInsertIndex; + const HBGlyphID16 *glyphs = &insertionAction[start]; + if (unlikely (!c->sanitizer.check_array (glyphs, count))) count = 0; + + bool before = flags & MarkedInsertBefore; + + unsigned int end = buffer->out_len; + if (unlikely (!buffer->move_to (mark))) return; + + if (buffer->idx < buffer->len && !before) + if (unlikely (!buffer->copy_glyph ())) return; + /* TODO We ignore KashidaLike setting. */ + if (unlikely (!buffer->replace_glyphs (0, count, glyphs))) return; + if (buffer->idx < buffer->len && !before) + buffer->skip_glyph (); + + if (unlikely (!buffer->move_to (end + count))) return; + + buffer->unsafe_to_break_from_outbuffer (mark, hb_min (buffer->idx + 1, buffer->len)); + } + + if (flags & SetMark) + mark = mark_loc; + + if (entry.data.currentInsertIndex != 0xFFFF) + { + unsigned int count = (flags & CurrentInsertCount) >> 5; + if (unlikely ((buffer->max_ops -= count) <= 0)) return; + unsigned int start = entry.data.currentInsertIndex; + const HBGlyphID16 *glyphs = &insertionAction[start]; + if (unlikely (!c->sanitizer.check_array (glyphs, count))) count = 0; + + bool before = flags & CurrentInsertBefore; + + unsigned int end = buffer->out_len; + + if (buffer->idx < buffer->len && !before) + if (unlikely (!buffer->copy_glyph ())) return; + /* TODO We ignore KashidaLike setting. */ + if (unlikely (!buffer->replace_glyphs (0, count, glyphs))) return; + if (buffer->idx < buffer->len && !before) + buffer->skip_glyph (); + + /* 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. + * + * https://github.com/harfbuzz/harfbuzz/issues/1224#issuecomment-427691417 + */ + if (unlikely (!buffer->move_to ((flags & DontAdvance) ? end : end + count))) return; + } + } + + public: + bool ret; + private: + hb_aat_apply_context_t *c; + unsigned int mark; + const UnsizedArrayOf &insertionAction; + }; + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + driver_context_t dc (this, c); + + StateTableDriver driver (machine, c->buffer, c->face); + driver.drive (&dc, c); + + return_trace (dc.ret); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + /* The rest of array sanitizations are done at run-time. */ + return_trace (c->check_struct (this) && machine.sanitize (c) && + insertionAction); + } + + protected: + StateTable + machine; + NNOffsetTo, HBUINT> + insertionAction; /* Byte offset from stateHeader to the start of + * the insertion glyph table. */ + public: + DEFINE_SIZE_STATIC (20); +}; + + +struct Feature +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + HBUINT16 featureType; /* The type of feature. */ + HBUINT16 featureSetting; /* The feature's setting (aka selector). */ + HBUINT32 enableFlags; /* Flags for the settings that this feature + * and setting enables. */ + HBUINT32 disableFlags; /* Complement of flags for the settings that this + * feature and setting disable. */ + + public: + DEFINE_SIZE_STATIC (12); +}; + +template +struct ChainSubtable +{ + typedef typename Types::HBUINT HBUINT; + + template + friend struct Chain; + + unsigned int get_size () const { return length; } + unsigned int get_type () const { return coverage & 0xFF; } + unsigned int get_coverage () const { return coverage >> (sizeof (HBUINT) * 8 - 8); } + + enum Coverage + { + Vertical = 0x80, /* If set, this subtable will only be applied + * to vertical text. If clear, this subtable + * will only be applied to horizontal text. */ + Backwards = 0x40, /* If set, this subtable will process glyphs + * in descending order. If clear, it will + * process the glyphs in ascending order. */ + AllDirections = 0x20, /* If set, this subtable will be applied to + * both horizontal and vertical text (i.e. + * the state of bit 0x80000000 is ignored). */ + Logical = 0x10, /* If set, this subtable will process glyphs + * in logical order (or reverse logical order, + * depending on the value of bit 0x80000000). */ + }; + enum Type + { + Rearrangement = 0, + Contextual = 1, + Ligature = 2, + Noncontextual = 4, + Insertion = 5 + }; + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + unsigned int subtable_type = get_type (); + TRACE_DISPATCH (this, subtable_type); + switch (subtable_type) { + case Rearrangement: return_trace (c->dispatch (u.rearrangement, std::forward (ds)...)); + case Contextual: return_trace (c->dispatch (u.contextual, std::forward (ds)...)); + case Ligature: return_trace (c->dispatch (u.ligature, std::forward (ds)...)); + case Noncontextual: return_trace (c->dispatch (u.noncontextual, std::forward (ds)...)); + case Insertion: return_trace (c->dispatch (u.insertion, std::forward (ds)...)); + default: return_trace (c->default_return_value ()); + } + } + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_sanitize_with_object_t with (&c->sanitizer, this); + return_trace (dispatch (c)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!length.sanitize (c) || + length <= min_size || + !c->check_range (this, length)) + return_trace (false); + + hb_sanitize_with_object_t with (c, this); + return_trace (dispatch (c)); + } + + protected: + HBUINT length; /* Total subtable length, including this header. */ + HBUINT coverage; /* Coverage flags and subtable type. */ + HBUINT32 subFeatureFlags;/* The 32-bit mask identifying which subtable this is. */ + union { + RearrangementSubtable rearrangement; + ContextualSubtable contextual; + LigatureSubtable ligature; + NoncontextualSubtable noncontextual; + InsertionSubtable insertion; + } u; + public: + DEFINE_SIZE_MIN (2 * sizeof (HBUINT) + 4); +}; + +template +struct Chain +{ + typedef typename Types::HBUINT HBUINT; + + hb_mask_t compile_flags (const hb_aat_map_builder_t *map) const + { + hb_mask_t flags = defaultFlags; + { + unsigned int count = featureCount; + for (unsigned i = 0; i < count; i++) + { + const Feature &feature = featureZ[i]; + hb_aat_layout_feature_type_t type = (hb_aat_layout_feature_type_t) (unsigned int) feature.featureType; + hb_aat_layout_feature_selector_t setting = (hb_aat_layout_feature_selector_t) (unsigned int) feature.featureSetting; + retry: + // Check whether this type/setting pair was requested in the map, and if so, apply its flags. + // (The search here only looks at the type and setting fields of feature_info_t.) + hb_aat_map_builder_t::feature_info_t info = { type, setting, false, 0 }; + if (map->current_features.bsearch (info)) + { + flags &= feature.disableFlags; + flags |= feature.enableFlags; + } + else if (type == HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE && setting == HB_AAT_LAYOUT_FEATURE_SELECTOR_SMALL_CAPS) + { + /* Deprecated. https://github.com/harfbuzz/harfbuzz/issues/1342 */ + type = HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE; + setting = HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS; + goto retry; + } +#ifndef HB_NO_AAT + else if (type == HB_AAT_LAYOUT_FEATURE_TYPE_LANGUAGE_TAG_TYPE && setting && + /* TODO: Rudimentary language matching. */ + hb_language_matches (map->face->table.ltag->get_language (setting - 1), map->props.language)) + { + flags &= feature.disableFlags; + flags |= feature.enableFlags; + } +#endif + } + } + return flags; + } + + void apply (hb_aat_apply_context_t *c) const + { + const ChainSubtable *subtable = &StructAfter> (featureZ.as_array (featureCount)); + unsigned int count = subtableCount; + for (unsigned int i = 0; i < count; i++) + { + bool reverse; + + if (hb_none (hb_iter (c->range_flags) | + hb_map ([&subtable] (const hb_aat_map_t::range_flags_t _) -> bool { return subtable->subFeatureFlags & (_.flags); }))) + goto skip; + c->subtable_flags = subtable->subFeatureFlags; + + if (!(subtable->get_coverage() & ChainSubtable::AllDirections) && + HB_DIRECTION_IS_VERTICAL (c->buffer->props.direction) != + bool (subtable->get_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->get_coverage () & ChainSubtable::Logical ? + bool (subtable->get_coverage () & ChainSubtable::Backwards) : + bool (subtable->get_coverage () & ChainSubtable::Backwards) != + HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction); + + if (!c->buffer->message (c->font, "start chainsubtable %u", c->lookup_index)) + goto skip; + + if (reverse) + c->buffer->reverse (); + + subtable->apply (c); + + if (reverse) + c->buffer->reverse (); + + (void) c->buffer->message (c->font, "end chainsubtable %u", c->lookup_index); + + if (unlikely (!c->buffer->successful)) return; + + skip: + subtable = &StructAfter> (*subtable); + c->set_lookup_index (c->lookup_index + 1); + } + } + + unsigned int get_size () const { return length; } + + bool sanitize (hb_sanitize_context_t *c, unsigned int version HB_UNUSED) const + { + TRACE_SANITIZE (this); + if (!length.sanitize (c) || + length < min_size || + !c->check_range (this, length)) + return_trace (false); + + if (!c->check_array (featureZ.arrayZ, featureCount)) + return_trace (false); + + const ChainSubtable *subtable = &StructAfter> (featureZ.as_array (featureCount)); + unsigned int count = subtableCount; + for (unsigned int i = 0; i < count; i++) + { + if (!subtable->sanitize (c)) + return_trace (false); + subtable = &StructAfter> (*subtable); + } + + return_trace (true); + } + + protected: + HBUINT32 defaultFlags; /* The default specification for subtables. */ + HBUINT32 length; /* Total byte count, including this header. */ + HBUINT featureCount; /* Number of feature subtable entries. */ + HBUINT subtableCount; /* The number of subtables in the chain. */ + + UnsizedArrayOf featureZ; /* Features. */ +/*ChainSubtable firstSubtable;*//* Subtables. */ +/*subtableGlyphCoverageArray*/ /* Only if version >= 3. We don't use. */ + + public: + DEFINE_SIZE_MIN (8 + 2 * sizeof (HBUINT)); +}; + + +/* + * The 'mort'/'morx' Table + */ + +template +struct mortmorx +{ + static constexpr hb_tag_t tableTag = TAG; + + bool has_data () const { return version != 0; } + + void compile_flags (const hb_aat_map_builder_t *mapper, + hb_aat_map_t *map) const + { + const Chain *chain = &firstChain; + unsigned int count = chainCount; + if (unlikely (!map->chain_flags.resize (count))) + return; + for (unsigned int i = 0; i < count; i++) + { + map->chain_flags[i].push (hb_aat_map_t::range_flags_t {chain->compile_flags (mapper), + mapper->range_first, + mapper->range_last}); + chain = &StructAfter> (*chain); + } + } + + void apply (hb_aat_apply_context_t *c, + const hb_aat_map_t &map) const + { + if (unlikely (!c->buffer->successful)) return; + + c->buffer->unsafe_to_concat (); + + c->set_lookup_index (0); + const Chain *chain = &firstChain; + unsigned int count = chainCount; + for (unsigned int i = 0; i < count; i++) + { + c->range_flags = &map.chain_flags[i]; + chain->apply (c); + if (unlikely (!c->buffer->successful)) return; + chain = &StructAfter> (*chain); + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!version.sanitize (c) || !version || !chainCount.sanitize (c)) + return_trace (false); + + const Chain *chain = &firstChain; + unsigned int count = chainCount; + for (unsigned int i = 0; i < count; i++) + { + if (!chain->sanitize (c, version)) + return_trace (false); + chain = &StructAfter> (*chain); + } + + return_trace (true); + } + + protected: + HBUINT16 version; /* Version number of the glyph metamorphosis table. + * 1, 2, or 3. */ + HBUINT16 unused; /* Set to 0. */ + HBUINT32 chainCount; /* Number of metamorphosis chains contained in this + * table. */ + Chain firstChain; /* Chains. */ + + public: + DEFINE_SIZE_MIN (8); +}; + +struct morx : mortmorx {}; +struct mort : mortmorx {}; + + +} /* namespace AAT */ + + +#endif /* HB_AAT_LAYOUT_MORX_TABLE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-opbd-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-opbd-table.hh new file mode 100644 index 00000000000..51b650fc33c --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-opbd-table.hh @@ -0,0 +1,173 @@ +/* + * Copyright © 2019 Ebrahim Byagowi + * + * 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. + */ + +#ifndef HB_AAT_LAYOUT_OPBD_TABLE_HH +#define HB_AAT_LAYOUT_OPBD_TABLE_HH + +#include "hb-aat-layout-common.hh" +#include "hb-open-type.hh" + +/* + * opbd -- Optical Bounds + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6opbd.html + */ +#define HB_AAT_TAG_opbd HB_TAG('o','p','b','d') + + +namespace AAT { + +struct OpticalBounds +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + FWORD leftSide; + FWORD topSide; + FWORD rightSide; + FWORD bottomSide; + public: + DEFINE_SIZE_STATIC (8); +}; + +struct opbdFormat0 +{ + bool get_bounds (hb_font_t *font, hb_codepoint_t glyph_id, + hb_glyph_extents_t *extents, const void *base) const + { + const Offset16To *bounds_offset = lookupTable.get_value (glyph_id, font->face->get_num_glyphs ()); + if (!bounds_offset) return false; + const OpticalBounds &bounds = base+*bounds_offset; + + if (extents) + *extents = { + font->em_scale_x (bounds.leftSide), + font->em_scale_y (bounds.topSide), + font->em_scale_x (bounds.rightSide), + font->em_scale_y (bounds.bottomSide) + }; + return true; + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && lookupTable.sanitize (c, base))); + } + + protected: + Lookup> + lookupTable; /* Lookup table associating glyphs with the four + * int16 values for the left-side, top-side, + * right-side, and bottom-side optical bounds. */ + public: + DEFINE_SIZE_MIN (2); +}; + +struct opbdFormat1 +{ + bool get_bounds (hb_font_t *font, hb_codepoint_t glyph_id, + hb_glyph_extents_t *extents, const void *base) const + { + const Offset16To *bounds_offset = lookupTable.get_value (glyph_id, font->face->get_num_glyphs ()); + if (!bounds_offset) return false; + const OpticalBounds &bounds = base+*bounds_offset; + + hb_position_t left = 0, top = 0, right = 0, bottom = 0, ignore; + if (font->get_glyph_contour_point (glyph_id, bounds.leftSide, &left, &ignore) || + font->get_glyph_contour_point (glyph_id, bounds.topSide, &ignore, &top) || + font->get_glyph_contour_point (glyph_id, bounds.rightSide, &right, &ignore) || + font->get_glyph_contour_point (glyph_id, bounds.bottomSide, &ignore, &bottom)) + { + if (extents) + *extents = {left, top, right, bottom}; + return true; + } + return false; + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && lookupTable.sanitize (c, base))); + } + + protected: + Lookup> + lookupTable; /* Lookup table associating glyphs with the four + * int16 values for the left-side, top-side, + * right-side, and bottom-side optical bounds. */ + public: + DEFINE_SIZE_MIN (2); +}; + +struct opbd +{ + static constexpr hb_tag_t tableTag = HB_AAT_TAG_opbd; + + bool get_bounds (hb_font_t *font, hb_codepoint_t glyph_id, + hb_glyph_extents_t *extents) const + { + switch (format) + { + case 0: return u.format0.get_bounds (font, glyph_id, extents, this); + case 1: return u.format1.get_bounds (font, glyph_id, extents, this); + default:return false; + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this) || version.major != 1)) + return_trace (false); + + switch (format) + { + case 0: return_trace (u.format0.sanitize (c, this)); + case 1: return_trace (u.format1.sanitize (c, this)); + default:return_trace (true); + } + } + + protected: + FixedVersion<>version; /* Version number of the optical bounds + * table (0x00010000 for the current version). */ + HBUINT16 format; /* Format of the optical bounds table. + * Format 0 indicates distance and Format 1 indicates + * control point. */ + union { + opbdFormat0 format0; + opbdFormat1 format1; + } u; + public: + DEFINE_SIZE_MIN (8); +}; + +} /* namespace AAT */ + + +#endif /* HB_AAT_LAYOUT_OPBD_TABLE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-trak-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-trak-table.hh new file mode 100644 index 00000000000..c72c0865d3e --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout-trak-table.hh @@ -0,0 +1,230 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * 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): Behdad Esfahbod + */ + +#ifndef HB_AAT_LAYOUT_TRAK_TABLE_HH +#define HB_AAT_LAYOUT_TRAK_TABLE_HH + +#include "hb-aat-layout-common.hh" +#include "hb-ot-layout.hh" +#include "hb-open-type.hh" + +/* + * trak -- Tracking + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6trak.html + */ +#define HB_AAT_TAG_trak HB_TAG('t','r','a','k') + + +namespace AAT { + + +struct TrackTableEntry +{ + friend struct TrackData; + + float get_track_value () const { return track.to_float (); } + + int get_value (const void *base, unsigned int index, + unsigned int table_size) const + { return (base+valuesZ).as_array (table_size)[index]; } + + public: + bool sanitize (hb_sanitize_context_t *c, const void *base, + unsigned int table_size) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + (valuesZ.sanitize (c, base, table_size)))); + } + + protected: + F16DOT16 track; /* Track value for this record. */ + NameID trackNameID; /* The 'name' table index for this track. + * (a short word or phrase like "loose" + * or "very tight") */ + NNOffset16To> + valuesZ; /* Offset from start of tracking table to + * per-size tracking values for this track. */ + + public: + DEFINE_SIZE_STATIC (8); +}; + +struct TrackData +{ + float interpolate_at (unsigned int idx, + float target_size, + const TrackTableEntry &trackTableEntry, + const void *base) const + { + unsigned int sizes = nSizes; + hb_array_t size_table ((base+sizeTable).arrayZ, sizes); + + float s0 = size_table[idx].to_float (); + float s1 = size_table[idx + 1].to_float (); + float t = unlikely (s0 == s1) ? 0.f : (target_size - s0) / (s1 - s0); + return t * trackTableEntry.get_value (base, idx + 1, sizes) + + (1.f - t) * trackTableEntry.get_value (base, idx, sizes); + } + + int get_tracking (const void *base, float ptem) const + { + /* + * Choose track. + */ + const TrackTableEntry *trackTableEntry = nullptr; + unsigned int count = nTracks; + for (unsigned int i = 0; i < count; i++) + { + /* Note: Seems like the track entries are sorted by values. But the + * spec doesn't explicitly say that. It just mentions it in the example. */ + + /* For now we only seek for track entries with zero tracking value */ + + if (trackTable[i].get_track_value () == 0.f) + { + trackTableEntry = &trackTable[i]; + break; + } + } + if (!trackTableEntry) return 0; + + /* + * Choose size. + */ + unsigned int sizes = nSizes; + if (!sizes) return 0; + if (sizes == 1) return trackTableEntry->get_value (base, 0, sizes); + + hb_array_t size_table ((base+sizeTable).arrayZ, sizes); + unsigned int size_index; + for (size_index = 0; size_index < sizes - 1; size_index++) + if (size_table[size_index].to_float () >= ptem) + break; + + return roundf (interpolate_at (size_index ? size_index - 1 : 0, ptem, + *trackTableEntry, base)); + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + sizeTable.sanitize (c, base, nSizes) && + trackTable.sanitize (c, nTracks, base, nSizes))); + } + + protected: + HBUINT16 nTracks; /* Number of separate tracks included in this table. */ + HBUINT16 nSizes; /* Number of point sizes included in this table. */ + NNOffset32To> + sizeTable; /* Offset from start of the tracking table to + * Array[nSizes] of size values.. */ + UnsizedArrayOf + trackTable; /* Array[nTracks] of TrackTableEntry records. */ + + public: + DEFINE_SIZE_ARRAY (8, trackTable); +}; + +struct trak +{ + static constexpr hb_tag_t tableTag = HB_AAT_TAG_trak; + + bool has_data () const { return version.to_int (); } + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + hb_mask_t trak_mask = c->plan->trak_mask; + + const float ptem = c->font->ptem; + if (unlikely (ptem <= 0.f)) + return_trace (false); + + hb_buffer_t *buffer = c->buffer; + if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction)) + { + const TrackData &trackData = this+horizData; + int tracking = trackData.get_tracking (this, ptem); + hb_position_t offset_to_add = c->font->em_scalef_x (tracking / 2); + hb_position_t advance_to_add = c->font->em_scalef_x (tracking); + foreach_grapheme (buffer, start, end) + { + if (!(buffer->info[start].mask & trak_mask)) continue; + buffer->pos[start].x_advance += advance_to_add; + buffer->pos[start].x_offset += offset_to_add; + } + } + else + { + const TrackData &trackData = this+vertData; + int tracking = trackData.get_tracking (this, ptem); + hb_position_t offset_to_add = c->font->em_scalef_y (tracking / 2); + hb_position_t advance_to_add = c->font->em_scalef_y (tracking); + foreach_grapheme (buffer, start, end) + { + if (!(buffer->info[start].mask & trak_mask)) continue; + buffer->pos[start].y_advance += advance_to_add; + buffer->pos[start].y_offset += offset_to_add; + } + } + + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + + return_trace (likely (c->check_struct (this) && + version.major == 1 && + horizData.sanitize (c, this, this) && + vertData.sanitize (c, this, this))); + } + + protected: + FixedVersion<>version; /* Version of the tracking table + * (0x00010000u for version 1.0). */ + HBUINT16 format; /* Format of the tracking table (set to 0). */ + Offset16To + horizData; /* Offset from start of tracking table to TrackData + * for horizontal text (or 0 if none). */ + Offset16To + vertData; /* Offset from start of tracking table to TrackData + * for vertical text (or 0 if none). */ + HBUINT16 reserved; /* Reserved. Set to 0. */ + + public: + DEFINE_SIZE_STATIC (12); +}; + +} /* namespace AAT */ + + +#endif /* HB_AAT_LAYOUT_TRAK_TABLE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-aat-layout.cc b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout.cc new file mode 100644 index 00000000000..5e4cea22249 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout.cc @@ -0,0 +1,446 @@ +/* + * Copyright © 2017 Google, Inc. + * Copyright © 2018 Ebrahim Byagowi + * + * 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): Behdad Esfahbod + */ + +#include "hb.hh" + +#include "hb-aat-layout.hh" +#include "hb-aat-layout-ankr-table.hh" +#include "hb-aat-layout-bsln-table.hh" // Just so we compile it; unused otherwise. +#include "hb-aat-layout-feat-table.hh" +#include "hb-aat-layout-just-table.hh" // Just so we compile it; unused otherwise. +#include "hb-aat-layout-kerx-table.hh" +#include "hb-aat-layout-morx-table.hh" +#include "hb-aat-layout-trak-table.hh" +#include "hb-aat-ltag-table.hh" + + +/* + * hb_aat_apply_context_t + */ + +/* Note: This context is used for kerning, even without AAT, hence the condition. */ +#if !defined(HB_NO_AAT) || !defined(HB_NO_OT_KERN) + +AAT::hb_aat_apply_context_t::hb_aat_apply_context_t (const hb_ot_shape_plan_t *plan_, + hb_font_t *font_, + hb_buffer_t *buffer_, + hb_blob_t *blob) : + plan (plan_), + font (font_), + face (font->face), + buffer (buffer_), + sanitizer (), + ankr_table (&Null (AAT::ankr)), + gdef_table ( +#ifndef HB_NO_OT_LAYOUT + face->table.GDEF->table +#else + &Null (GDEF) +#endif + ), + lookup_index (0) +{ + sanitizer.init (blob); + sanitizer.set_num_glyphs (face->get_num_glyphs ()); + sanitizer.start_processing (); + sanitizer.set_max_ops (HB_SANITIZE_MAX_OPS_MAX); +} + +AAT::hb_aat_apply_context_t::~hb_aat_apply_context_t () +{ sanitizer.end_processing (); } + +void +AAT::hb_aat_apply_context_t::set_ankr_table (const AAT::ankr *ankr_table_) +{ ankr_table = ankr_table_; } + +#endif + + +/** + * SECTION:hb-aat-layout + * @title: hb-aat-layout + * @short_description: Apple Advanced Typography Layout + * @include: hb-aat.h + * + * Functions for querying AAT Layout features in the font face. + * + * HarfBuzz supports all of the AAT tables used to implement shaping. Other + * AAT tables and their associated features are not supported. + **/ + + +#if !defined(HB_NO_AAT) || defined(HAVE_CORETEXT) + +/* Mapping from OpenType feature tags to AAT feature names and selectors. + * + * Table data courtesy of Apple. Converted from mnemonics to integers + * when moving to this file. */ +static const hb_aat_feature_mapping_t feature_mappings[] = +{ + {HB_TAG ('a','f','r','c'), HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS, HB_AAT_LAYOUT_FEATURE_SELECTOR_VERTICAL_FRACTIONS, HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_FRACTIONS}, + {HB_TAG ('c','2','p','c'), HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE, HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_PETITE_CAPS, HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_UPPER_CASE}, + {HB_TAG ('c','2','s','c'), HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE, HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_SMALL_CAPS, HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_UPPER_CASE}, + {HB_TAG ('c','a','l','t'), HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_ALTERNATES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_ALTERNATES_OFF}, + {HB_TAG ('c','a','s','e'), HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT, HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_LAYOUT_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_LAYOUT_OFF}, + {HB_TAG ('c','l','i','g'), HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES, HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_LIGATURES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_LIGATURES_OFF}, + {HB_TAG ('c','p','s','p'), HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT, HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_SPACING_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_SPACING_OFF}, + {HB_TAG ('c','s','w','h'), HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_SWASH_ALTERNATES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_SWASH_ALTERNATES_OFF}, + {HB_TAG ('d','l','i','g'), HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES, HB_AAT_LAYOUT_FEATURE_SELECTOR_RARE_LIGATURES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_RARE_LIGATURES_OFF}, + {HB_TAG ('e','x','p','t'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_EXPERT_CHARACTERS, (hb_aat_layout_feature_selector_t) 16}, + {HB_TAG ('f','r','a','c'), HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS, HB_AAT_LAYOUT_FEATURE_SELECTOR_DIAGONAL_FRACTIONS, HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_FRACTIONS}, + {HB_TAG ('f','w','i','d'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_MONOSPACED_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('h','a','l','t'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_HALF_WIDTH_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('h','i','s','t'), (hb_aat_layout_feature_type_t) 40, (hb_aat_layout_feature_selector_t) 0, (hb_aat_layout_feature_selector_t) 1}, + {HB_TAG ('h','k','n','a'), HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_HORIZ_KANA_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_HORIZ_KANA_OFF}, + {HB_TAG ('h','l','i','g'), HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES, HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_OFF}, + {HB_TAG ('h','n','g','l'), HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION, HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL, HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_TRANSLITERATION}, + {HB_TAG ('h','o','j','o'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_HOJO_CHARACTERS, (hb_aat_layout_feature_selector_t) 16}, + {HB_TAG ('h','w','i','d'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_HALF_WIDTH_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('i','t','a','l'), HB_AAT_LAYOUT_FEATURE_TYPE_ITALIC_CJK_ROMAN, HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_OFF}, + {HB_TAG ('j','p','0','4'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS2004_CHARACTERS, (hb_aat_layout_feature_selector_t) 16}, + {HB_TAG ('j','p','7','8'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1978_CHARACTERS, (hb_aat_layout_feature_selector_t) 16}, + {HB_TAG ('j','p','8','3'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1983_CHARACTERS, (hb_aat_layout_feature_selector_t) 16}, + {HB_TAG ('j','p','9','0'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1990_CHARACTERS, (hb_aat_layout_feature_selector_t) 16}, + {HB_TAG ('l','i','g','a'), HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES, HB_AAT_LAYOUT_FEATURE_SELECTOR_COMMON_LIGATURES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_COMMON_LIGATURES_OFF}, + {HB_TAG ('l','n','u','m'), HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_CASE, HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_NUMBERS, (hb_aat_layout_feature_selector_t) 2}, + {HB_TAG ('m','g','r','k'), HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS, HB_AAT_LAYOUT_FEATURE_SELECTOR_MATHEMATICAL_GREEK_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_MATHEMATICAL_GREEK_OFF}, + {HB_TAG ('n','l','c','k'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_NLCCHARACTERS, (hb_aat_layout_feature_selector_t) 16}, + {HB_TAG ('o','n','u','m'), HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_CASE, HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_NUMBERS, (hb_aat_layout_feature_selector_t) 2}, + {HB_TAG ('o','r','d','n'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION, HB_AAT_LAYOUT_FEATURE_SELECTOR_ORDINALS, HB_AAT_LAYOUT_FEATURE_SELECTOR_NORMAL_POSITION}, + {HB_TAG ('p','a','l','t'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_PROPORTIONAL_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('p','c','a','p'), HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE, HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_PETITE_CAPS, HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_LOWER_CASE}, + {HB_TAG ('p','k','n','a'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('p','n','u','m'), HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_NUMBERS, (hb_aat_layout_feature_selector_t) 4}, + {HB_TAG ('p','w','i','d'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('q','w','i','d'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_QUARTER_WIDTH_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('r','l','i','g'), HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES, HB_AAT_LAYOUT_FEATURE_SELECTOR_REQUIRED_LIGATURES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_REQUIRED_LIGATURES_OFF}, + {HB_TAG ('r','u','b','y'), HB_AAT_LAYOUT_FEATURE_TYPE_RUBY_KANA, HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_OFF}, + {HB_TAG ('s','i','n','f'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION, HB_AAT_LAYOUT_FEATURE_SELECTOR_SCIENTIFIC_INFERIORS, HB_AAT_LAYOUT_FEATURE_SELECTOR_NORMAL_POSITION}, + {HB_TAG ('s','m','c','p'), HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE, HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS, HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_LOWER_CASE}, + {HB_TAG ('s','m','p','l'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_SIMPLIFIED_CHARACTERS, (hb_aat_layout_feature_selector_t) 16}, + {HB_TAG ('s','s','0','1'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ONE_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ONE_OFF}, + {HB_TAG ('s','s','0','2'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWO_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWO_OFF}, + {HB_TAG ('s','s','0','3'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THREE_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THREE_OFF}, + {HB_TAG ('s','s','0','4'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOUR_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOUR_OFF}, + {HB_TAG ('s','s','0','5'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIVE_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIVE_OFF}, + {HB_TAG ('s','s','0','6'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIX_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIX_OFF}, + {HB_TAG ('s','s','0','7'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVEN_OFF}, + {HB_TAG ('s','s','0','8'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHT_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHT_OFF}, + {HB_TAG ('s','s','0','9'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINE_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINE_OFF}, + {HB_TAG ('s','s','1','0'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TEN_OFF}, + {HB_TAG ('s','s','1','1'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ELEVEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ELEVEN_OFF}, + {HB_TAG ('s','s','1','2'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWELVE_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWELVE_OFF}, + {HB_TAG ('s','s','1','3'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THIRTEEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THIRTEEN_OFF}, + {HB_TAG ('s','s','1','4'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOURTEEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOURTEEN_OFF}, + {HB_TAG ('s','s','1','5'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIFTEEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIFTEEN_OFF}, + {HB_TAG ('s','s','1','6'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIXTEEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIXTEEN_OFF}, + {HB_TAG ('s','s','1','7'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVENTEEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVENTEEN_OFF}, + {HB_TAG ('s','s','1','8'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHTEEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHTEEN_OFF}, + {HB_TAG ('s','s','1','9'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINETEEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINETEEN_OFF}, + {HB_TAG ('s','s','2','0'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWENTY_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWENTY_OFF}, + {HB_TAG ('s','u','b','s'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION, HB_AAT_LAYOUT_FEATURE_SELECTOR_INFERIORS, HB_AAT_LAYOUT_FEATURE_SELECTOR_NORMAL_POSITION}, + {HB_TAG ('s','u','p','s'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION, HB_AAT_LAYOUT_FEATURE_SELECTOR_SUPERIORS, HB_AAT_LAYOUT_FEATURE_SELECTOR_NORMAL_POSITION}, + {HB_TAG ('s','w','s','h'), HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_SWASH_ALTERNATES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_SWASH_ALTERNATES_OFF}, + {HB_TAG ('t','i','t','l'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS, HB_AAT_LAYOUT_FEATURE_SELECTOR_TITLING_CAPS, HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_STYLE_OPTIONS}, + {HB_TAG ('t','n','a','m'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_NAMES_CHARACTERS, (hb_aat_layout_feature_selector_t) 16}, + {HB_TAG ('t','n','u','m'), HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_MONOSPACED_NUMBERS, (hb_aat_layout_feature_selector_t) 4}, + {HB_TAG ('t','r','a','d'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_CHARACTERS, (hb_aat_layout_feature_selector_t) 16}, + {HB_TAG ('t','w','i','d'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_THIRD_WIDTH_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('u','n','i','c'), HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE, (hb_aat_layout_feature_selector_t) 14, (hb_aat_layout_feature_selector_t) 15}, + {HB_TAG ('v','a','l','t'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_PROPORTIONAL_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('v','e','r','t'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION, HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_OFF}, + {HB_TAG ('v','h','a','l'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_HALF_WIDTH_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('v','k','n','a'), HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_VERT_KANA_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_VERT_KANA_OFF}, + {HB_TAG ('v','p','a','l'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_PROPORTIONAL_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('v','r','t','2'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION, HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_OFF}, + {HB_TAG ('v','r','t','r'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION, (hb_aat_layout_feature_selector_t) 2, (hb_aat_layout_feature_selector_t) 3}, + {HB_TAG ('z','e','r','o'), HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS, HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASHED_ZERO_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASHED_ZERO_OFF}, +}; + +/** + * hb_aat_layout_find_feature_mapping: + * @tag: The requested #hb_tag_t feature tag + * + * Fetches the AAT feature-and-selector combination that corresponds + * to a given OpenType feature tag. + * + * Return value: the AAT features and selectors corresponding to the + * OpenType feature tag queried + * + **/ +const hb_aat_feature_mapping_t * +hb_aat_layout_find_feature_mapping (hb_tag_t tag) +{ + return hb_sorted_array (feature_mappings).bsearch (tag); +} +#endif + + +#ifndef HB_NO_AAT + +/* + * mort/morx/kerx/trak + */ + + +void +hb_aat_layout_compile_map (const hb_aat_map_builder_t *mapper, + hb_aat_map_t *map) +{ + const AAT::morx& morx = *mapper->face->table.morx; + if (morx.has_data ()) + { + morx.compile_flags (mapper, map); + return; + } + + const AAT::mort& mort = *mapper->face->table.mort; + if (mort.has_data ()) + { + mort.compile_flags (mapper, map); + return; + } +} + + +/** + * hb_aat_layout_has_substitution: + * @face: #hb_face_t to work upon + * + * Tests whether the specified face includes any substitutions in the + * `morx` or `mort` tables. + * + * Note: does not examine the `GSUB` table. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 2.3.0 + */ +hb_bool_t +hb_aat_layout_has_substitution (hb_face_t *face) +{ + return face->table.morx->has_data () || + face->table.mort->has_data (); +} + +void +hb_aat_layout_substitute (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned num_features) +{ + hb_aat_map_builder_t builder (font->face, plan->props); + for (unsigned i = 0; i < num_features; i++) + builder.add_feature (features[i]); + hb_aat_map_t map; + builder.compile (map); + + hb_blob_t *morx_blob = font->face->table.morx.get_blob (); + const AAT::morx& morx = *morx_blob->as (); + if (morx.has_data ()) + { + AAT::hb_aat_apply_context_t c (plan, font, buffer, morx_blob); + if (!buffer->message (font, "start table morx")) return; + morx.apply (&c, map); + (void) buffer->message (font, "end table morx"); + return; + } + + hb_blob_t *mort_blob = font->face->table.mort.get_blob (); + const AAT::mort& mort = *mort_blob->as (); + if (mort.has_data ()) + { + AAT::hb_aat_apply_context_t c (plan, font, buffer, mort_blob); + if (!buffer->message (font, "start table mort")) return; + mort.apply (&c, map); + (void) buffer->message (font, "end table mort"); + return; + } +} + +void +hb_aat_layout_zero_width_deleted_glyphs (hb_buffer_t *buffer) +{ + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + hb_glyph_position_t *pos = buffer->pos; + for (unsigned int i = 0; i < count; i++) + if (unlikely (info[i].codepoint == AAT::DELETED_GLYPH)) + pos[i].x_advance = pos[i].y_advance = pos[i].x_offset = pos[i].y_offset = 0; +} + +static bool +is_deleted_glyph (const hb_glyph_info_t *info) +{ + return info->codepoint == AAT::DELETED_GLYPH; +} + +void +hb_aat_layout_remove_deleted_glyphs (hb_buffer_t *buffer) +{ + buffer->delete_glyphs_inplace (is_deleted_glyph); +} + +/** + * hb_aat_layout_has_positioning: + * @face: #hb_face_t to work upon + * + * Tests whether the specified face includes any positioning information + * in the `kerx` table. + * + * Note: does not examine the `GPOS` table. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 2.3.0 + */ +hb_bool_t +hb_aat_layout_has_positioning (hb_face_t *face) +{ + return face->table.kerx->has_data (); +} + +void +hb_aat_layout_position (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + hb_blob_t *kerx_blob = font->face->table.kerx.get_blob (); + const AAT::kerx& kerx = *kerx_blob->as (); + + AAT::hb_aat_apply_context_t c (plan, font, buffer, kerx_blob); + if (!buffer->message (font, "start table kerx")) return; + c.set_ankr_table (font->face->table.ankr.get ()); + kerx.apply (&c); + (void) buffer->message (font, "end table kerx"); +} + + +/** + * hb_aat_layout_has_tracking: + * @face:: #hb_face_t to work upon + * + * Tests whether the specified face includes any tracking information + * in the `trak` table. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 2.3.0 + */ +hb_bool_t +hb_aat_layout_has_tracking (hb_face_t *face) +{ + return face->table.trak->has_data (); +} + +void +hb_aat_layout_track (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + const AAT::trak& trak = *font->face->table.trak; + + AAT::hb_aat_apply_context_t c (plan, font, buffer); + trak.apply (&c); +} + +/** + * hb_aat_layout_get_feature_types: + * @face: #hb_face_t to work upon + * @start_offset: offset of the first feature type to retrieve + * @feature_count: (inout) (optional): Input = the maximum number of feature types to return; + * Output = the actual number of feature types returned (may be zero) + * @features: (out caller-allocates) (array length=feature_count): Array of feature types found + * + * Fetches a list of the AAT feature types included in the specified face. + * + * Return value: Number of all available feature types. + * + * Since: 2.2.0 + */ +unsigned int +hb_aat_layout_get_feature_types (hb_face_t *face, + unsigned int start_offset, + unsigned int *feature_count, /* IN/OUT. May be NULL. */ + hb_aat_layout_feature_type_t *features /* OUT. May be NULL. */) +{ + return face->table.feat->get_feature_types (start_offset, feature_count, features); +} + +/** + * hb_aat_layout_feature_type_get_name_id: + * @face: #hb_face_t to work upon + * @feature_type: The #hb_aat_layout_feature_type_t of the requested feature type + * + * Fetches the name identifier of the specified feature type in the face's `name` table. + * + * Return value: Name identifier of the requested feature type + * + * Since: 2.2.0 + */ +hb_ot_name_id_t +hb_aat_layout_feature_type_get_name_id (hb_face_t *face, + hb_aat_layout_feature_type_t feature_type) +{ + return face->table.feat->get_feature_name_id (feature_type); +} + +/** + * hb_aat_layout_feature_type_get_selector_infos: + * @face: #hb_face_t to work upon + * @feature_type: The #hb_aat_layout_feature_type_t of the requested feature type + * @start_offset: offset of the first feature type to retrieve + * @selector_count: (inout) (optional): Input = the maximum number of selectors to return; + * Output = the actual number of selectors returned (may be zero) + * @selectors: (out caller-allocates) (array length=selector_count) (optional): + * A buffer pointer. The selectors available for the feature type queries. + * @default_index: (out) (optional): The index of the feature's default selector, if any + * + * Fetches a list of the selectors available for the specified feature in the given face. + * + * If upon return, @default_index is set to #HB_AAT_LAYOUT_NO_SELECTOR_INDEX, then + * the feature type is non-exclusive. Otherwise, @default_index is the index of + * the selector that is selected by default. + * + * Return value: Number of all available feature selectors + * + * Since: 2.2.0 + */ +unsigned int +hb_aat_layout_feature_type_get_selector_infos (hb_face_t *face, + hb_aat_layout_feature_type_t feature_type, + unsigned int start_offset, + unsigned int *selector_count, /* IN/OUT. May be NULL. */ + hb_aat_layout_feature_selector_info_t *selectors, /* OUT. May be NULL. */ + unsigned int *default_index /* OUT. May be NULL. */) +{ + return face->table.feat->get_selector_infos (feature_type, start_offset, selector_count, selectors, default_index); +} + + +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-aat-layout.h b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout.h new file mode 100644 index 00000000000..9af27400886 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout.h @@ -0,0 +1,795 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * + * 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. + */ + +#if !defined(HB_AAT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_AAT_LAYOUT_H +#define HB_AAT_LAYOUT_H + +#include "hb.h" + +#include "hb-ot.h" + +HB_BEGIN_DECLS + +/** + * hb_aat_layout_feature_type_t: + * @HB_AAT_LAYOUT_FEATURE_TYPE_INVALID: Initial, unset feature type + * @HB_AAT_LAYOUT_FEATURE_TYPE_ALL_TYPOGRAPHIC: [All Typographic Features](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type0) + * @HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES: [Ligatures](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type1) + * @HB_AAT_LAYOUT_FEATURE_TYPE_CURISVE_CONNECTION: [Cursive Connection](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type2) + * @HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE: [Letter Case](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type3) + * @HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION: [Vertical Substitution](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type4) + * @HB_AAT_LAYOUT_FEATURE_TYPE_LINGUISTIC_REARRANGEMENT: [Linguistic Rearrangement](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type5) + * @HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING: [Number Spacing](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type6) + * @HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE: [Smart Swash](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type8) + * @HB_AAT_LAYOUT_FEATURE_TYPE_DIACRITICS_TYPE: [Diacritics](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type9) + * @HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION: [Vertical Position](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type10) + * @HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS: [Fractions](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type11) + * @HB_AAT_LAYOUT_FEATURE_TYPE_OVERLAPPING_CHARACTERS_TYPE: [Overlapping Characters](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type13) + * @HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS: [Typographic Extras](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type14) + * @HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS: [Mathematical Extras](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type15) + * @HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE: [Ornament Sets](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type16) + * @HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES: [Character Alternatives](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type17) + * @HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE: [Design Complexity](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type18) + * @HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS: [Style Options](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type19) + * @HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE: [Character Shape](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type20) + * @HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_CASE: [Number Case](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type21) + * @HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING: [Text Spacing](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type22) + * @HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION: [Transliteration](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type23) + * @HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE: [Annotation](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type24) + * @HB_AAT_LAYOUT_FEATURE_TYPE_KANA_SPACING_TYPE: [Kana Spacing](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type25) + * @HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_SPACING_TYPE: [Ideographic Spacing](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type26) + * @HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE: [Unicode Decomposition](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type27) + * @HB_AAT_LAYOUT_FEATURE_TYPE_RUBY_KANA: [Ruby Kana](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type28) + * @HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE: [CJK Symbol Alternatives](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type29) + * @HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE: [Ideographic Alternatives](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type30) + * @HB_AAT_LAYOUT_FEATURE_TYPE_CJK_VERTICAL_ROMAN_PLACEMENT_TYPE: [CJK Vertical Roman Placement](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type31) + * @HB_AAT_LAYOUT_FEATURE_TYPE_ITALIC_CJK_ROMAN: [Italic CJK Roman](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type32) + * @HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT: [Case Sensitive Layout](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type33) + * @HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA: [Alternate Kana](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type34) + * @HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES: [Stylistic Alternatives](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type35) + * @HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES: [Contextual Alternatives](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type36) + * @HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE: [Lower Case](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type37) + * @HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE: [Upper Case](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type38) + * @HB_AAT_LAYOUT_FEATURE_TYPE_LANGUAGE_TAG_TYPE: [Language Tag](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type39) + * @HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE: [CJK Roman Spacing](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type103) + * + * The possible feature types defined for AAT shaping, from Apple [Font Feature Registry](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html). + * + * Since: 2.2.0 + */ +typedef enum +{ + HB_AAT_LAYOUT_FEATURE_TYPE_INVALID = 0xFFFF, + + HB_AAT_LAYOUT_FEATURE_TYPE_ALL_TYPOGRAPHIC = 0, + HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES = 1, + HB_AAT_LAYOUT_FEATURE_TYPE_CURISVE_CONNECTION = 2, + HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE = 3, + HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION = 4, + HB_AAT_LAYOUT_FEATURE_TYPE_LINGUISTIC_REARRANGEMENT = 5, + HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING = 6, + HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE = 8, + HB_AAT_LAYOUT_FEATURE_TYPE_DIACRITICS_TYPE = 9, + HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION = 10, + HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS = 11, + HB_AAT_LAYOUT_FEATURE_TYPE_OVERLAPPING_CHARACTERS_TYPE = 13, + HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS = 14, + HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS = 15, + HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE = 16, + HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES = 17, + HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE = 18, + HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS = 19, + HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE = 20, + HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_CASE = 21, + HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING = 22, + HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION = 23, + HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE = 24, + HB_AAT_LAYOUT_FEATURE_TYPE_KANA_SPACING_TYPE = 25, + HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_SPACING_TYPE = 26, + HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE = 27, + HB_AAT_LAYOUT_FEATURE_TYPE_RUBY_KANA = 28, + HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE = 29, + HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE = 30, + HB_AAT_LAYOUT_FEATURE_TYPE_CJK_VERTICAL_ROMAN_PLACEMENT_TYPE = 31, + HB_AAT_LAYOUT_FEATURE_TYPE_ITALIC_CJK_ROMAN = 32, + HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT = 33, + HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA = 34, + HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES = 35, + HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES = 36, + HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE = 37, + HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE = 38, + HB_AAT_LAYOUT_FEATURE_TYPE_LANGUAGE_TAG_TYPE = 39, + HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE = 103, + + /*< private >*/ + _HB_AAT_LAYOUT_FEATURE_TYPE_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/ +} hb_aat_layout_feature_type_t; + +/** + * hb_aat_layout_feature_selector_t: + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_INVALID: Initial, unset feature selector + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_TYPE_FEATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_ALL_TYPOGRAPHIC + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_TYPE_FEATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_ALL_TYPOGRAPHIC + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_REQUIRED_LIGATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_REQUIRED_LIGATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_COMMON_LIGATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_COMMON_LIGATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_RARE_LIGATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_RARE_LIGATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_LOGOS_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_LOGOS_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_REBUS_PICTURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_REBUS_PICTURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DIPHTHONG_LIGATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DIPHTHONG_LIGATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SQUARED_LIGATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SQUARED_LIGATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ABBREV_SQUARED_LIGATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ABBREV_SQUARED_LIGATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SYMBOL_LIGATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SYMBOL_LIGATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_LIGATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_LIGATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_UNCONNECTED: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_PARTIALLY_CONNECTED: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CURSIVE: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_AND_LOWER_CASE: Deprecated + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_CAPS: Deprecated + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_LOWER_CASE: Deprecated + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SMALL_CAPS: Deprecated + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_INITIAL_CAPS: Deprecated + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_INITIAL_CAPS_AND_SMALL_CAPS: Deprecated + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_LINGUISTIC_REARRANGEMENT_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LINGUISTIC_REARRANGEMENT + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_LINGUISTIC_REARRANGEMENT_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LINGUISTIC_REARRANGEMENT + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_MONOSPACED_NUMBERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_NUMBERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_THIRD_WIDTH_NUMBERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_QUARTER_WIDTH_NUMBERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_INITIAL_SWASHES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_INITIAL_SWASHES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_FINAL_SWASHES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_FINAL_SWASHES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_INITIAL_SWASHES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_INITIAL_SWASHES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_FINAL_SWASHES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_FINAL_SWASHES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NON_FINAL_SWASHES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NON_FINAL_SWASHES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SHOW_DIACRITICS: for #HB_AAT_LAYOUT_FEATURE_TYPE_DIACRITICS_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HIDE_DIACRITICS: for #HB_AAT_LAYOUT_FEATURE_TYPE_DIACRITICS_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DECOMPOSE_DIACRITICS: for #HB_AAT_LAYOUT_FEATURE_TYPE_DIACRITICS_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NORMAL_POSITION: for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SUPERIORS: for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_INFERIORS: for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ORDINALS: for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SCIENTIFIC_INFERIORS: for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_FRACTIONS: for #HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_VERTICAL_FRACTIONS: for #HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DIAGONAL_FRACTIONS: for #HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_PREVENT_OVERLAP_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_OVERLAPPING_CHARACTERS_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_PREVENT_OVERLAP_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_OVERLAPPING_CHARACTERS_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHENS_TO_EM_DASH_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHENS_TO_EM_DASH_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_EN_DASH_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_EN_DASH_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASHED_ZERO_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASHED_ZERO_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_FORM_INTERROBANG_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_FORM_INTERROBANG_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SMART_QUOTES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SMART_QUOTES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_PERIODS_TO_ELLIPSIS_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_PERIODS_TO_ELLIPSIS_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_MINUS_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_MINUS_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ASTERISK_TO_MULTIPLY_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ASTERISK_TO_MULTIPLY_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASH_TO_DIVIDE_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASH_TO_DIVIDE_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_INEQUALITY_LIGATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_INEQUALITY_LIGATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_EXPONENTS_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_EXPONENTS_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_MATHEMATICAL_GREEK_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_MATHEMATICAL_GREEK_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_ORNAMENTS: for #HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DINGBATS: for #HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_PI_CHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_FLEURONS: for #HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DECORATIVE_BORDERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_INTERNATIONAL_SYMBOLS: for #HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_MATH_SYMBOLS: for #HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_ALTERNATES: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL1: for #HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL2: for #HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL3: for #HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL4: for #HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL5: for #HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_STYLE_OPTIONS: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DISPLAY_TEXT: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ENGRAVED_TEXT: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ILLUMINATED_CAPS: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_TITLING_CAPS: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_TALL_CAPS: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_CHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SIMPLIFIED_CHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1978_CHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1983_CHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1990_CHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_ONE: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_TWO: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_THREE: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_FOUR: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_FIVE: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_EXPERT_CHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS2004_CHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HOJO_CHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NLCCHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_NAMES_CHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_NUMBERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_CASE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_NUMBERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_CASE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_TEXT: for #HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_MONOSPACED_TEXT: for #HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HALF_WIDTH_TEXT: for #HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_THIRD_WIDTH_TEXT: for #HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_QUARTER_WIDTH_TEXT: for #HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_PROPORTIONAL_TEXT: for #HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_HALF_WIDTH_TEXT: for #HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_TRANSLITERATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL: for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HIRAGANA_TO_KATAKANA: for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_KATAKANA_TO_HIRAGANA: for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_KANA_TO_ROMANIZATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ROMANIZATION_TO_HIRAGANA: for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ROMANIZATION_TO_KATAKANA: for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL_ALT_ONE: for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL_ALT_TWO: for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL_ALT_THREE: for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_BOX_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ROUNDED_BOX_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CIRCLE_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_INVERTED_CIRCLE_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_PARENTHESIS_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_PERIOD_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ROMAN_NUMERAL_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DIAMOND_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_INVERTED_BOX_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_INVERTED_ROUNDED_BOX_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_FULL_WIDTH_KANA: for #HB_AAT_LAYOUT_FEATURE_TYPE_KANA_SPACING_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_KANA: for #HB_AAT_LAYOUT_FEATURE_TYPE_KANA_SPACING_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_FULL_WIDTH_IDEOGRAPHS: for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_SPACING_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_IDEOGRAPHS: for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_SPACING_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HALF_WIDTH_IDEOGRAPHS: for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_SPACING_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CANONICAL_COMPOSITION_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CANONICAL_COMPOSITION_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_COMPATIBILITY_COMPOSITION_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_COMPATIBILITY_COMPOSITION_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_TRANSCODING_COMPOSITION_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_TRANSCODING_COMPOSITION_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_RUBY_KANA: Deprecated; use #HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_OFF instead + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA: Deprecated; use #HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_ON instead + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_RUBY_KANA + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_RUBY_KANA + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_CJK_SYMBOL_ALTERNATIVES: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_ONE: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_TWO: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_THREE: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_FOUR: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_FIVE: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_IDEOGRAPHIC_ALTERNATIVES: for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_ONE: for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_TWO: for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_THREE: for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_FOUR: for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_FIVE: for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_VERTICAL_ROMAN_CENTERED: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_VERTICAL_ROMAN_PLACEMENT_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_VERTICAL_ROMAN_HBASELINE: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_VERTICAL_ROMAN_PLACEMENT_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_CJK_ITALIC_ROMAN: Deprecated; use #HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_OFF instead + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN: Deprecated; use #HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_ON instead + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_ITALIC_CJK_ROMAN + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_ITALIC_CJK_ROMAN + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_LAYOUT_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_LAYOUT_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_SPACING_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_SPACING_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_HORIZ_KANA_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_HORIZ_KANA_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_VERT_KANA_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_VERT_KANA_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_STYLISTIC_ALTERNATES: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ONE_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ONE_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWO_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWO_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THREE_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THREE_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOUR_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOUR_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIVE_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIVE_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIX_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIX_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVEN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVEN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHT_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHT_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINE_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINE_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TEN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TEN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ELEVEN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ELEVEN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWELVE_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWELVE_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THIRTEEN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THIRTEEN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOURTEEN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOURTEEN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIFTEEN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIFTEEN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIXTEEN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIXTEEN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVENTEEN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVENTEEN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHTEEN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHTEEN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINETEEN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINETEEN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWENTY_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWENTY_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_ALTERNATES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_ALTERNATES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SWASH_ALTERNATES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SWASH_ALTERNATES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_SWASH_ALTERNATES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_SWASH_ALTERNATES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_LOWER_CASE: for #HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS: for #HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_PETITE_CAPS: for #HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_UPPER_CASE: for #HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_SMALL_CAPS: for #HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_PETITE_CAPS: for #HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HALF_WIDTH_CJK_ROMAN: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_CJK_ROMAN: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_CJK_ROMAN: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_FULL_WIDTH_CJK_ROMAN: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE + * + * The selectors defined for specifying AAT feature settings. + * + * Since: 2.2.0 + */ +typedef enum +{ + HB_AAT_LAYOUT_FEATURE_SELECTOR_INVALID = 0xFFFF, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_ALL_TYPOGRAPHIC */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_TYPE_FEATURES_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_TYPE_FEATURES_OFF = 1, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_REQUIRED_LIGATURES_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_REQUIRED_LIGATURES_OFF = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_COMMON_LIGATURES_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_COMMON_LIGATURES_OFF = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_RARE_LIGATURES_ON = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_RARE_LIGATURES_OFF = 5, + HB_AAT_LAYOUT_FEATURE_SELECTOR_LOGOS_ON = 6, + HB_AAT_LAYOUT_FEATURE_SELECTOR_LOGOS_OFF = 7, + HB_AAT_LAYOUT_FEATURE_SELECTOR_REBUS_PICTURES_ON = 8, + HB_AAT_LAYOUT_FEATURE_SELECTOR_REBUS_PICTURES_OFF = 9, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DIPHTHONG_LIGATURES_ON = 10, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DIPHTHONG_LIGATURES_OFF = 11, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SQUARED_LIGATURES_ON = 12, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SQUARED_LIGATURES_OFF = 13, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ABBREV_SQUARED_LIGATURES_ON = 14, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ABBREV_SQUARED_LIGATURES_OFF = 15, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SYMBOL_LIGATURES_ON = 16, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SYMBOL_LIGATURES_OFF = 17, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_LIGATURES_ON = 18, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_LIGATURES_OFF = 19, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_ON = 20, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_OFF = 21, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_UNCONNECTED = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PARTIALLY_CONNECTED = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CURSIVE = 2, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_AND_LOWER_CASE = 0, /* deprecated */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_CAPS = 1, /* deprecated */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_LOWER_CASE = 2, /* deprecated */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_SMALL_CAPS = 3, /* deprecated */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_INITIAL_CAPS = 4, /* deprecated */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_INITIAL_CAPS_AND_SMALL_CAPS = 5, /* deprecated */ + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_OFF = 1, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_LINGUISTIC_REARRANGEMENT */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_LINGUISTIC_REARRANGEMENT_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_LINGUISTIC_REARRANGEMENT_OFF = 1, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_MONOSPACED_NUMBERS = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_NUMBERS = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_THIRD_WIDTH_NUMBERS = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_QUARTER_WIDTH_NUMBERS = 3, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_INITIAL_SWASHES_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_INITIAL_SWASHES_OFF = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_FINAL_SWASHES_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_FINAL_SWASHES_OFF = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_INITIAL_SWASHES_ON = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_INITIAL_SWASHES_OFF = 5, + HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_FINAL_SWASHES_ON = 6, + HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_FINAL_SWASHES_OFF = 7, + HB_AAT_LAYOUT_FEATURE_SELECTOR_NON_FINAL_SWASHES_ON = 8, + HB_AAT_LAYOUT_FEATURE_SELECTOR_NON_FINAL_SWASHES_OFF = 9, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_DIACRITICS_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_SHOW_DIACRITICS = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HIDE_DIACRITICS = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DECOMPOSE_DIACRITICS = 2, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NORMAL_POSITION = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SUPERIORS = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_INFERIORS = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ORDINALS = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SCIENTIFIC_INFERIORS = 4, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_FRACTIONS = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_VERTICAL_FRACTIONS = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DIAGONAL_FRACTIONS = 2, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_OVERLAPPING_CHARACTERS_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_PREVENT_OVERLAP_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PREVENT_OVERLAP_OFF = 1, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHENS_TO_EM_DASH_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHENS_TO_EM_DASH_OFF = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_EN_DASH_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_EN_DASH_OFF = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASHED_ZERO_ON = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASHED_ZERO_OFF = 5, + HB_AAT_LAYOUT_FEATURE_SELECTOR_FORM_INTERROBANG_ON = 6, + HB_AAT_LAYOUT_FEATURE_SELECTOR_FORM_INTERROBANG_OFF = 7, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SMART_QUOTES_ON = 8, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SMART_QUOTES_OFF = 9, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PERIODS_TO_ELLIPSIS_ON = 10, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PERIODS_TO_ELLIPSIS_OFF = 11, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_MINUS_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_MINUS_OFF = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ASTERISK_TO_MULTIPLY_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ASTERISK_TO_MULTIPLY_OFF = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASH_TO_DIVIDE_ON = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASH_TO_DIVIDE_OFF = 5, + HB_AAT_LAYOUT_FEATURE_SELECTOR_INEQUALITY_LIGATURES_ON = 6, + HB_AAT_LAYOUT_FEATURE_SELECTOR_INEQUALITY_LIGATURES_OFF = 7, + HB_AAT_LAYOUT_FEATURE_SELECTOR_EXPONENTS_ON = 8, + HB_AAT_LAYOUT_FEATURE_SELECTOR_EXPONENTS_OFF = 9, + HB_AAT_LAYOUT_FEATURE_SELECTOR_MATHEMATICAL_GREEK_ON = 10, + HB_AAT_LAYOUT_FEATURE_SELECTOR_MATHEMATICAL_GREEK_OFF = 11, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_ORNAMENTS = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DINGBATS = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PI_CHARACTERS = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_FLEURONS = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DECORATIVE_BORDERS = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_INTERNATIONAL_SYMBOLS = 5, + HB_AAT_LAYOUT_FEATURE_SELECTOR_MATH_SYMBOLS = 6, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_ALTERNATES = 0, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL1 = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL2 = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL3 = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL4 = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL5 = 4, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_STYLE_OPTIONS = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DISPLAY_TEXT = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ENGRAVED_TEXT = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ILLUMINATED_CAPS = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_TITLING_CAPS = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_TALL_CAPS = 5, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_CHARACTERS = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SIMPLIFIED_CHARACTERS = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1978_CHARACTERS = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1983_CHARACTERS = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1990_CHARACTERS = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_ONE = 5, + HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_TWO = 6, + HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_THREE = 7, + HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_FOUR = 8, + HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_FIVE = 9, + HB_AAT_LAYOUT_FEATURE_SELECTOR_EXPERT_CHARACTERS = 10, + HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS2004_CHARACTERS = 11, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HOJO_CHARACTERS = 12, + HB_AAT_LAYOUT_FEATURE_SELECTOR_NLCCHARACTERS = 13, + HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_NAMES_CHARACTERS = 14, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_CASE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_NUMBERS = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_NUMBERS = 1, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_TEXT = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_MONOSPACED_TEXT = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HALF_WIDTH_TEXT = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_THIRD_WIDTH_TEXT = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_QUARTER_WIDTH_TEXT = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_PROPORTIONAL_TEXT = 5, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_HALF_WIDTH_TEXT = 6, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_TRANSLITERATION = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HIRAGANA_TO_KATAKANA = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_KATAKANA_TO_HIRAGANA = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_KANA_TO_ROMANIZATION = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ROMANIZATION_TO_HIRAGANA = 5, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ROMANIZATION_TO_KATAKANA = 6, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL_ALT_ONE = 7, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL_ALT_TWO = 8, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL_ALT_THREE = 9, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_ANNOTATION = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_BOX_ANNOTATION = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ROUNDED_BOX_ANNOTATION = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CIRCLE_ANNOTATION = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_INVERTED_CIRCLE_ANNOTATION = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PARENTHESIS_ANNOTATION = 5, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PERIOD_ANNOTATION = 6, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ROMAN_NUMERAL_ANNOTATION = 7, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DIAMOND_ANNOTATION = 8, + HB_AAT_LAYOUT_FEATURE_SELECTOR_INVERTED_BOX_ANNOTATION = 9, + HB_AAT_LAYOUT_FEATURE_SELECTOR_INVERTED_ROUNDED_BOX_ANNOTATION= 10, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_KANA_SPACING_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_FULL_WIDTH_KANA = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_KANA = 1, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_SPACING_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_FULL_WIDTH_IDEOGRAPHS = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_IDEOGRAPHS = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HALF_WIDTH_IDEOGRAPHS = 2, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_CANONICAL_COMPOSITION_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CANONICAL_COMPOSITION_OFF = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_COMPATIBILITY_COMPOSITION_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_COMPATIBILITY_COMPOSITION_OFF = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_TRANSCODING_COMPOSITION_ON = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_TRANSCODING_COMPOSITION_OFF = 5, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_RUBY_KANA */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_RUBY_KANA = 0, /* deprecated - use HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_OFF instead */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA = 1, /* deprecated - use HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_ON instead */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_OFF = 3, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_CJK_SYMBOL_ALTERNATIVES = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_ONE = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_TWO = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_THREE = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_FOUR = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_FIVE = 5, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_IDEOGRAPHIC_ALTERNATIVES = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_ONE = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_TWO = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_THREE = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_FOUR = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_FIVE = 5, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_VERTICAL_ROMAN_PLACEMENT_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_VERTICAL_ROMAN_CENTERED = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_VERTICAL_ROMAN_HBASELINE = 1, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_ITALIC_CJK_ROMAN */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_CJK_ITALIC_ROMAN = 0, /* deprecated - use HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_OFF instead */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN = 1, /* deprecated - use HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_ON instead */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_OFF = 3, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_LAYOUT_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_LAYOUT_OFF = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_SPACING_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_SPACING_OFF = 3, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_HORIZ_KANA_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_HORIZ_KANA_OFF = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_VERT_KANA_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_VERT_KANA_OFF = 3, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_STYLISTIC_ALTERNATES = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ONE_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ONE_OFF = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWO_ON = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWO_OFF = 5, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THREE_ON = 6, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THREE_OFF = 7, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOUR_ON = 8, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOUR_OFF = 9, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIVE_ON = 10, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIVE_OFF = 11, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIX_ON = 12, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIX_OFF = 13, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVEN_ON = 14, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVEN_OFF = 15, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHT_ON = 16, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHT_OFF = 17, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINE_ON = 18, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINE_OFF = 19, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TEN_ON = 20, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TEN_OFF = 21, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ELEVEN_ON = 22, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ELEVEN_OFF = 23, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWELVE_ON = 24, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWELVE_OFF = 25, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THIRTEEN_ON = 26, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THIRTEEN_OFF = 27, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOURTEEN_ON = 28, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOURTEEN_OFF = 29, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIFTEEN_ON = 30, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIFTEEN_OFF = 31, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIXTEEN_ON = 32, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIXTEEN_OFF = 33, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVENTEEN_ON = 34, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVENTEEN_OFF = 35, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHTEEN_ON = 36, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHTEEN_OFF = 37, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINETEEN_ON = 38, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINETEEN_OFF = 39, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWENTY_ON = 40, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWENTY_OFF = 41, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_ALTERNATES_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_ALTERNATES_OFF = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SWASH_ALTERNATES_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SWASH_ALTERNATES_OFF = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_SWASH_ALTERNATES_ON = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_SWASH_ALTERNATES_OFF= 5, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_LOWER_CASE = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_PETITE_CAPS = 2, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_UPPER_CASE = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_SMALL_CAPS = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_PETITE_CAPS = 2, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_HALF_WIDTH_CJK_ROMAN = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_CJK_ROMAN = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_CJK_ROMAN = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_FULL_WIDTH_CJK_ROMAN = 3, + + /*< private >*/ + _HB_AAT_LAYOUT_FEATURE_SELECTOR_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/ +} hb_aat_layout_feature_selector_t; + +HB_EXTERN unsigned int +hb_aat_layout_get_feature_types (hb_face_t *face, + unsigned int start_offset, + unsigned int *feature_count, /* IN/OUT. May be NULL. */ + hb_aat_layout_feature_type_t *features /* OUT. May be NULL. */); + +HB_EXTERN hb_ot_name_id_t +hb_aat_layout_feature_type_get_name_id (hb_face_t *face, + hb_aat_layout_feature_type_t feature_type); + +/** + * hb_aat_layout_feature_selector_info_t: + * @name_id: The selector's name identifier + * @enable: The value to turn the selector on + * @disable: The value to turn the selector off + * + * Structure representing a setting for an #hb_aat_layout_feature_type_t. + */ +typedef struct hb_aat_layout_feature_selector_info_t { + hb_ot_name_id_t name_id; + hb_aat_layout_feature_selector_t enable; + hb_aat_layout_feature_selector_t disable; + /*< private >*/ + unsigned int reserved; +} hb_aat_layout_feature_selector_info_t; + +/** + * HB_AAT_LAYOUT_NO_SELECTOR_INDEX + * + * Used when getting or setting AAT feature selectors. Indicates that + * there is no selector index corresponding to the selector of interest. + * + */ +#define HB_AAT_LAYOUT_NO_SELECTOR_INDEX 0xFFFFu + +HB_EXTERN unsigned int +hb_aat_layout_feature_type_get_selector_infos (hb_face_t *face, + hb_aat_layout_feature_type_t feature_type, + unsigned int start_offset, + unsigned int *selector_count, /* IN/OUT. May be NULL. */ + hb_aat_layout_feature_selector_info_t *selectors, /* OUT. May be NULL. */ + unsigned int *default_index /* OUT. May be NULL. */); + + +/* + * morx/mort + */ + +HB_EXTERN hb_bool_t +hb_aat_layout_has_substitution (hb_face_t *face); + + +/* + * kerx + */ + +HB_EXTERN hb_bool_t +hb_aat_layout_has_positioning (hb_face_t *face); + + +/* + * trak + */ + +HB_EXTERN hb_bool_t +hb_aat_layout_has_tracking (hb_face_t *face); + + +HB_END_DECLS + +#endif /* HB_AAT_LAYOUT_H */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-aat-layout.hh b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout.hh new file mode 100644 index 00000000000..15c382aa92d --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-aat-layout.hh @@ -0,0 +1,77 @@ +/* + * Copyright © 2017 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): Behdad Esfahbod + */ + +#ifndef HB_AAT_LAYOUT_HH +#define HB_AAT_LAYOUT_HH + +#include "hb.hh" + +#include "hb-ot-shape.hh" +#include "hb-aat-ltag-table.hh" + +struct hb_aat_feature_mapping_t +{ + hb_tag_t otFeatureTag; + hb_aat_layout_feature_type_t aatFeatureType; + hb_aat_layout_feature_selector_t selectorToEnable; + hb_aat_layout_feature_selector_t selectorToDisable; + + int cmp (hb_tag_t key) const + { return key < otFeatureTag ? -1 : key > otFeatureTag ? 1 : 0; } +}; + +HB_INTERNAL const hb_aat_feature_mapping_t * +hb_aat_layout_find_feature_mapping (hb_tag_t tag); + +HB_INTERNAL void +hb_aat_layout_compile_map (const hb_aat_map_builder_t *mapper, + hb_aat_map_t *map); + +HB_INTERNAL void +hb_aat_layout_substitute (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned num_features); + +HB_INTERNAL void +hb_aat_layout_zero_width_deleted_glyphs (hb_buffer_t *buffer); + +HB_INTERNAL void +hb_aat_layout_remove_deleted_glyphs (hb_buffer_t *buffer); + +HB_INTERNAL void +hb_aat_layout_position (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + +HB_INTERNAL void +hb_aat_layout_track (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + + +#endif /* HB_AAT_LAYOUT_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-aat-ltag-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-aat-ltag-table.hh new file mode 100644 index 00000000000..6d771e1513e --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-aat-ltag-table.hh @@ -0,0 +1,92 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * + * 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. + */ + +#ifndef HB_AAT_LTAG_TABLE_HH +#define HB_AAT_LTAG_TABLE_HH + +#include "hb-open-type.hh" + +/* + * ltag -- Language Tag + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6ltag.html + */ +#define HB_AAT_TAG_ltag HB_TAG('l','t','a','g') + + +namespace AAT { + +using namespace OT; + + +struct FTStringRange +{ + friend struct ltag; + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && (base+tag).sanitize (c, length)); + } + + protected: + NNOffset16To> + tag; /* Offset from the start of the table to + * the beginning of the string */ + HBUINT16 length; /* String length (in bytes) */ + public: + DEFINE_SIZE_STATIC (4); +}; + +struct ltag +{ + static constexpr hb_tag_t tableTag = HB_AAT_TAG_ltag; + + hb_language_t get_language (unsigned int i) const + { + const FTStringRange &range = tagRanges[i]; + return hb_language_from_string ((const char *) (this+range.tag).arrayZ, + range.length); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + version >= 1 && + tagRanges.sanitize (c, this))); + } + + protected: + HBUINT32 version; /* Table version; currently 1 */ + HBUINT32 flags; /* Table flags; currently none defined */ + Array32Of + tagRanges; /* Range for each tag's string */ + public: + DEFINE_SIZE_ARRAY (12, tagRanges); +}; + +} /* namespace AAT */ + + +#endif /* HB_AAT_LTAG_TABLE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-aat-map.cc b/src/3rdparty/harfbuzz-ng/src/hb-aat-map.cc new file mode 100644 index 00000000000..5bdb8004f2f --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-aat-map.cc @@ -0,0 +1,172 @@ +/* + * Copyright © 2009,2010 Red Hat, Inc. + * Copyright © 2010,2011,2013 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#ifndef HB_NO_AAT_SHAPE + +#include "hb-aat-map.hh" + +#include "hb-aat-layout.hh" +#include "hb-aat-layout-feat-table.hh" + + +void hb_aat_map_builder_t::add_feature (const hb_feature_t &feature) +{ + if (!face->table.feat->has_data ()) return; + + if (feature.tag == HB_TAG ('a','a','l','t')) + { + if (!face->table.feat->exposes_feature (HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES)) + return; + feature_range_t *range = features.push(); + range->start = feature.start; + range->end = feature.end; + range->info.type = HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES; + range->info.setting = (hb_aat_layout_feature_selector_t) feature.value; + range->info.seq = features.length; + range->info.is_exclusive = true; + return; + } + + const hb_aat_feature_mapping_t *mapping = hb_aat_layout_find_feature_mapping (feature.tag); + if (!mapping) return; + + const AAT::FeatureName* feature_name = &face->table.feat->get_feature (mapping->aatFeatureType); + if (!feature_name->has_data ()) + { + /* Special case: Chain::compile_flags will fall back to the deprecated version of + * small-caps if necessary, so we need to check for that possibility. + * https://github.com/harfbuzz/harfbuzz/issues/2307 */ + if (mapping->aatFeatureType == HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE && + mapping->selectorToEnable == HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS) + { + feature_name = &face->table.feat->get_feature (HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE); + if (!feature_name->has_data ()) return; + } + else return; + } + + feature_range_t *range = features.push(); + range->start = feature.start; + range->end = feature.end; + range->info.type = mapping->aatFeatureType; + range->info.setting = feature.value ? mapping->selectorToEnable : mapping->selectorToDisable; + range->info.seq = features.length; + range->info.is_exclusive = feature_name->is_exclusive (); +} + +void +hb_aat_map_builder_t::compile (hb_aat_map_t &m) +{ + /* Compute active features per range, and compile each. */ + + /* Sort features by start/end events. */ + hb_vector_t feature_events; + for (unsigned int i = 0; i < features.length; i++) + { + auto &feature = features[i]; + + if (features[i].start == features[i].end) + continue; + + feature_event_t *event; + + event = feature_events.push (); + event->index = features[i].start; + event->start = true; + event->feature = feature.info; + + event = feature_events.push (); + event->index = features[i].end; + event->start = false; + event->feature = feature.info; + } + feature_events.qsort (); + /* Add a strategic final event. */ + { + feature_info_t feature; + feature.seq = features.length + 1; + + feature_event_t *event = feature_events.push (); + event->index = -1; /* This value does magic. */ + event->start = false; + event->feature = feature; + } + + /* Scan events and save features for each range. */ + hb_sorted_vector_t active_features; + unsigned int last_index = 0; + for (unsigned int i = 0; i < feature_events.length; i++) + { + feature_event_t *event = &feature_events[i]; + + if (event->index != last_index) + { + /* Save a snapshot of active features and the range. */ + + /* Sort features and merge duplicates */ + current_features = active_features; + range_first = last_index; + range_last = event->index - 1; + if (current_features.length) + { + current_features.qsort (); + unsigned int j = 0; + for (unsigned int i = 1; i < current_features.length; i++) + if (current_features[i].type != current_features[j].type || + /* Nonexclusive feature selectors come in even/odd pairs to turn a setting on/off + * respectively, so we mask out the low-order bit when checking for "duplicates" + * (selectors referring to the same feature setting) here. */ + (!current_features[i].is_exclusive && ((current_features[i].setting & ~1) != (current_features[j].setting & ~1)))) + current_features[++j] = current_features[i]; + current_features.shrink (j + 1); + } + + hb_aat_layout_compile_map (this, &m); + + last_index = event->index; + } + + if (event->start) + { + active_features.push (event->feature); + } else { + feature_info_t *feature = active_features.lsearch (event->feature); + if (feature) + active_features.remove_ordered (feature - active_features.arrayZ); + } + } + + for (auto &chain_flags : m.chain_flags) + // With our above setup this value is one less than desired; adjust it. + chain_flags.tail().cluster_last = HB_FEATURE_GLOBAL_END; +} + + +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-aat-map.hh b/src/3rdparty/harfbuzz-ng/src/hb-aat-map.hh new file mode 100644 index 00000000000..cb22ffee42d --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-aat-map.hh @@ -0,0 +1,123 @@ +/* + * 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): Behdad Esfahbod + */ + +#ifndef HB_AAT_MAP_HH +#define HB_AAT_MAP_HH + +#include "hb.hh" + + +struct hb_aat_map_t +{ + friend struct hb_aat_map_builder_t; + + public: + struct range_flags_t + { + hb_mask_t flags; + unsigned cluster_first; + unsigned cluster_last; // end - 1 + }; + + public: + hb_vector_t> chain_flags; +}; + +struct hb_aat_map_builder_t +{ + public: + + HB_INTERNAL hb_aat_map_builder_t (hb_face_t *face_, + const hb_segment_properties_t props_) : + face (face_), + props (props_) {} + + HB_INTERNAL void add_feature (const hb_feature_t &feature); + + HB_INTERNAL void compile (hb_aat_map_t &m); + + public: + struct feature_info_t + { + hb_aat_layout_feature_type_t type; + hb_aat_layout_feature_selector_t setting; + bool is_exclusive; + unsigned seq; /* For stable sorting only. */ + + HB_INTERNAL static int cmp (const void *pa, const void *pb) + { + const feature_info_t *a = (const feature_info_t *) pa; + const feature_info_t *b = (const feature_info_t *) pb; + if (a->type != b->type) return (a->type < b->type ? -1 : 1); + if (!a->is_exclusive && + (a->setting & ~1) != (b->setting & ~1)) return (a->setting < b->setting ? -1 : 1); + return (a->seq < b->seq ? -1 : a->seq > b->seq ? 1 : 0); + } + + /* compares type & setting only */ + int cmp (const feature_info_t& f) const + { + return (f.type != type) ? (f.type < type ? -1 : 1) : + (f.setting != setting) ? (f.setting < setting ? -1 : 1) : 0; + } + }; + + struct feature_range_t + { + feature_info_t info; + unsigned start; + unsigned end; + }; + + private: + struct feature_event_t + { + unsigned int index; + bool start; + feature_info_t feature; + + HB_INTERNAL static int cmp (const void *pa, const void *pb) { + const feature_event_t *a = (const feature_event_t *) pa; + const feature_event_t *b = (const feature_event_t *) pb; + return a->index < b->index ? -1 : a->index > b->index ? 1 : + a->start < b->start ? -1 : a->start > b->start ? 1 : + feature_info_t::cmp (&a->feature, &b->feature); + } + }; + + public: + hb_face_t *face; + hb_segment_properties_t props; + + public: + hb_sorted_vector_t features; + hb_sorted_vector_t current_features; + unsigned range_first = HB_FEATURE_GLOBAL_START; + unsigned range_last = HB_FEATURE_GLOBAL_END; +}; + + +#endif /* HB_AAT_MAP_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-aat.h b/src/3rdparty/harfbuzz-ng/src/hb-aat.h new file mode 100644 index 00000000000..c14313d1e2e --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-aat.h @@ -0,0 +1,38 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * + * 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. + */ + +#ifndef HB_AAT_H +#define HB_AAT_H +#define HB_AAT_H_IN + +#include "hb.h" + +#include "hb-aat-layout.h" + +HB_BEGIN_DECLS + +HB_END_DECLS + +#undef HB_AAT_H_IN +#endif /* HB_AAT_H */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-algs.hh b/src/3rdparty/harfbuzz-ng/src/hb-algs.hh new file mode 100644 index 00000000000..ea970571654 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-algs.hh @@ -0,0 +1,1542 @@ +/* + * Copyright © 2017 Google, Inc. + * Copyright © 2019 Facebook, 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): Behdad Esfahbod + * Facebook Author(s): Behdad Esfahbod + */ + +#ifndef HB_ALGS_HH +#define HB_ALGS_HH + +#include "hb.hh" +#include "hb-meta.hh" +#include "hb-null.hh" +#include "hb-number.hh" + +#include +#include +#include +#include + +/* + * Flags + */ + +/* Enable bitwise ops on enums marked as flags_t */ +/* To my surprise, looks like the function resolver is happy to silently cast + * one enum to another... So this doesn't provide the type-checking that I + * originally had in mind... :(. + * + * For MSVC warnings, see: https://github.com/harfbuzz/harfbuzz/pull/163 + */ +#ifdef _MSC_VER +# pragma warning(disable:4200) +# pragma warning(disable:4800) +#endif +#define HB_MARK_AS_FLAG_T(T) \ + extern "C++" { \ + static inline constexpr T operator | (T l, T r) { return T ((unsigned) l | (unsigned) r); } \ + static inline constexpr T operator & (T l, T r) { return T ((unsigned) l & (unsigned) r); } \ + static inline constexpr T operator ^ (T l, T r) { return T ((unsigned) l ^ (unsigned) r); } \ + static inline constexpr unsigned operator ~ (T r) { return (~(unsigned) r); } \ + static inline T& operator |= (T &l, T r) { l = l | r; return l; } \ + static inline T& operator &= (T& l, T r) { l = l & r; return l; } \ + static inline T& operator ^= (T& l, T r) { l = l ^ r; return l; } \ + } \ + static_assert (true, "") + +/* Useful for set-operations on small enums. + * For example, for testing "x ∈ {x1, x2, x3}" use: + * (FLAG_UNSAFE(x) & (FLAG(x1) | FLAG(x2) | FLAG(x3))) + */ +#define FLAG(x) (static_assert_expr ((unsigned)(x) < 32) + (((uint32_t) 1U) << (unsigned)(x))) +#define FLAG_UNSAFE(x) ((unsigned)(x) < 32 ? (((uint32_t) 1U) << (unsigned)(x)) : 0) +#define FLAG_RANGE(x,y) (static_assert_expr ((x) < (y)) + FLAG(y+1) - FLAG(x)) +#define FLAG64(x) (static_assert_expr ((unsigned)(x) < 64) + (((uint64_t) 1ULL) << (unsigned)(x))) +#define FLAG64_UNSAFE(x) ((unsigned)(x) < 64 ? (((uint64_t) 1ULL) << (unsigned)(x)) : 0) + + +/* + * Big-endian integers. + */ + +/* Endian swap, used in Windows related backends */ +static inline constexpr uint16_t hb_uint16_swap (uint16_t v) +{ return (v >> 8) | (v << 8); } +static inline constexpr uint32_t hb_uint32_swap (uint32_t v) +{ return (hb_uint16_swap (v) << 16) | hb_uint16_swap (v >> 16); } + +#ifndef HB_FAST_INT_ACCESS +#if defined(__OPTIMIZE__) && \ + defined(__BYTE_ORDER) && \ + (__BYTE_ORDER == __BIG_ENDIAN || \ + (__BYTE_ORDER == __LITTLE_ENDIAN && \ + hb_has_builtin(__builtin_bswap16) && \ + hb_has_builtin(__builtin_bswap32))) +#define HB_FAST_INT_ACCESS 1 +#else +#define HB_FAST_INT_ACCESS 0 +#endif +#endif + +template +struct BEInt; +template +struct BEInt +{ + public: + BEInt () = default; + constexpr BEInt (Type V) : v {uint8_t (V)} {} + constexpr operator Type () const { return v; } + private: uint8_t v; +}; +template +struct BEInt +{ + struct __attribute__((packed)) packed_uint16_t { uint16_t v; }; + + public: + BEInt () = default; + + BEInt (Type V) +#if HB_FAST_INT_ACCESS +#if __BYTE_ORDER == __LITTLE_ENDIAN + { ((packed_uint16_t *) v)->v = __builtin_bswap16 (V); } +#else /* __BYTE_ORDER == __BIG_ENDIAN */ + { ((packed_uint16_t *) v)->v = V; } +#endif +#else + : v {uint8_t ((V >> 8) & 0xFF), + uint8_t ((V ) & 0xFF)} {} +#endif + + constexpr operator Type () const { +#if HB_FAST_INT_ACCESS +#if __BYTE_ORDER == __LITTLE_ENDIAN + return __builtin_bswap16 (((packed_uint16_t *) v)->v); +#else /* __BYTE_ORDER == __BIG_ENDIAN */ + return ((packed_uint16_t *) v)->v; +#endif +#else + return (v[0] << 8) + + (v[1] ); +#endif + } + private: uint8_t v[2]; +}; +template +struct BEInt +{ + static_assert (!std::is_signed::value, ""); + public: + BEInt () = default; + constexpr BEInt (Type V) : v {uint8_t ((V >> 16) & 0xFF), + uint8_t ((V >> 8) & 0xFF), + uint8_t ((V ) & 0xFF)} {} + + constexpr operator Type () const { return (v[0] << 16) + + (v[1] << 8) + + (v[2] ); } + private: uint8_t v[3]; +}; +template +struct BEInt +{ + struct __attribute__((packed)) packed_uint32_t { uint32_t v; }; + + public: + BEInt () = default; + + BEInt (Type V) +#if HB_FAST_INT_ACCESS +#if __BYTE_ORDER == __LITTLE_ENDIAN + { ((packed_uint32_t *) v)->v = __builtin_bswap32 (V); } +#else /* __BYTE_ORDER == __BIG_ENDIAN */ + { ((packed_uint32_t *) v)->v = V; } +#endif +#else + : v {uint8_t ((V >> 24) & 0xFF), + uint8_t ((V >> 16) & 0xFF), + uint8_t ((V >> 8) & 0xFF), + uint8_t ((V ) & 0xFF)} {} +#endif + + constexpr operator Type () const { +#if HB_FAST_INT_ACCESS +#if __BYTE_ORDER == __LITTLE_ENDIAN + return __builtin_bswap32 (((packed_uint32_t *) v)->v); +#else /* __BYTE_ORDER == __BIG_ENDIAN */ + return ((packed_uint32_t *) v)->v; +#endif +#else + return (v[0] << 24) + + (v[1] << 16) + + (v[2] << 8) + + (v[3] ); +#endif + } + private: uint8_t v[4]; +}; + +/* Floats. */ + +/* We want our rounding towards +infinity. */ +static inline float +_hb_roundf (float x) { return floorf (x + .5f); } +#define roundf(x) _hb_roundf(x) + + +/* Encodes three unsigned integers in one 64-bit number. If the inputs have more than 21 bits, + * values will be truncated / overlap, and might not decode exactly. */ +#define HB_CODEPOINT_ENCODE3(x,y,z) (((uint64_t) (x) << 42) | ((uint64_t) (y) << 21) | (uint64_t) (z)) +#define HB_CODEPOINT_DECODE3_1(v) ((hb_codepoint_t) ((v) >> 42)) +#define HB_CODEPOINT_DECODE3_2(v) ((hb_codepoint_t) ((v) >> 21) & 0x1FFFFFu) +#define HB_CODEPOINT_DECODE3_3(v) ((hb_codepoint_t) (v) & 0x1FFFFFu) + +/* Custom encoding used by hb-ucd. */ +#define HB_CODEPOINT_ENCODE3_11_7_14(x,y,z) (((uint32_t) ((x) & 0x07FFu) << 21) | (((uint32_t) (y) & 0x007Fu) << 14) | (uint32_t) ((z) & 0x3FFFu)) +#define HB_CODEPOINT_DECODE3_11_7_14_1(v) ((hb_codepoint_t) ((v) >> 21)) +#define HB_CODEPOINT_DECODE3_11_7_14_2(v) ((hb_codepoint_t) (((v) >> 14) & 0x007Fu) | 0x0300) +#define HB_CODEPOINT_DECODE3_11_7_14_3(v) ((hb_codepoint_t) (v) & 0x3FFFu) + + +struct +{ + /* Note. This is dangerous in that if it's passed an rvalue, it returns rvalue-reference. */ + template constexpr auto + operator () (T&& v) const HB_AUTO_RETURN ( std::forward (v) ) +} +HB_FUNCOBJ (hb_identity); +struct +{ + /* Like identity(), but only retains lvalue-references. Rvalues are returned as rvalues. */ + template constexpr T& + operator () (T& v) const { return v; } + + template constexpr hb_remove_reference + operator () (T&& v) const { return v; } +} +HB_FUNCOBJ (hb_lidentity); +struct +{ + /* Like identity(), but always returns rvalue. */ + template constexpr hb_remove_reference + operator () (T&& v) const { return v; } +} +HB_FUNCOBJ (hb_ridentity); + +struct +{ + template constexpr bool + operator () (T&& v) const { return bool (std::forward (v)); } +} +HB_FUNCOBJ (hb_bool); + + +/* The MIT License + + Copyright (C) 2012 Zilong Tan (eric.zltan@gmail.com) + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + + +// Compression function for Merkle-Damgard construction. +// This function is generated using the framework provided. +#define mix(h) ( \ + (void) ((h) ^= (h) >> 23), \ + (void) ((h) *= 0x2127599bf4325c37ULL), \ + (h) ^= (h) >> 47) + +static inline uint64_t fasthash64(const void *buf, size_t len, uint64_t seed) +{ + struct __attribute__((packed)) packed_uint64_t { uint64_t v; }; + const uint64_t m = 0x880355f21e6d1965ULL; + const packed_uint64_t *pos = (const packed_uint64_t *)buf; + const packed_uint64_t *end = pos + (len / 8); + const unsigned char *pos2; + uint64_t h = seed ^ (len * m); + uint64_t v; + +#ifndef HB_OPTIMIZE_SIZE + if (((uintptr_t) pos & 7) == 0) + { + while (pos != end) + { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-align" + v = * (const uint64_t *) (pos++); +#pragma GCC diagnostic pop + h ^= mix(v); + h *= m; + } + } + else +#endif + { + while (pos != end) + { + v = pos++->v; + h ^= mix(v); + h *= m; + } + } + + pos2 = (const unsigned char*)pos; + v = 0; + + switch (len & 7) { + case 7: v ^= (uint64_t)pos2[6] << 48; HB_FALLTHROUGH; + case 6: v ^= (uint64_t)pos2[5] << 40; HB_FALLTHROUGH; + case 5: v ^= (uint64_t)pos2[4] << 32; HB_FALLTHROUGH; + case 4: v ^= (uint64_t)pos2[3] << 24; HB_FALLTHROUGH; + case 3: v ^= (uint64_t)pos2[2] << 16; HB_FALLTHROUGH; + case 2: v ^= (uint64_t)pos2[1] << 8; HB_FALLTHROUGH; + case 1: v ^= (uint64_t)pos2[0]; + h ^= mix(v); + h *= m; + } + + return mix(h); +} + +static inline uint32_t fasthash32(const void *buf, size_t len, uint32_t seed) +{ + // the following trick converts the 64-bit hashcode to Fermat + // residue, which shall retain information from both the higher + // and lower parts of hashcode. + uint64_t h = fasthash64(buf, len, seed); + return h - (h >> 32); +} + +struct +{ + private: + + template constexpr auto + impl (const T& v, hb_priority<2>) const HB_RETURN (uint32_t, hb_deref (v).hash ()) + + // Horrible: std:hash() of integers seems to be identity in gcc / clang?! + // https://github.com/harfbuzz/harfbuzz/pull/4228 + // + // For performance characteristics see: + // https://github.com/harfbuzz/harfbuzz/pull/4228#issuecomment-1565079537 + template ::value && sizeof (T) <= sizeof (uint32_t))> constexpr auto + impl (const T& v, hb_priority<1>) const HB_RETURN (uint32_t, (uint32_t) v * 2654435761u /* Knuh's multiplicative hash */) + template ::value && sizeof (T) > sizeof (uint32_t))> constexpr auto + impl (const T& v, hb_priority<1>) const HB_RETURN (uint32_t, (uint32_t) (v ^ (v >> 32)) * 2654435761u /* Knuth's multiplicative hash */) + + template ::value)> constexpr auto + impl (const T& v, hb_priority<1>) const HB_RETURN (uint32_t, fasthash32 (std::addressof (v), sizeof (T), 0xf437ffe6)) + + template constexpr auto + impl (const T& v, hb_priority<0>) const HB_RETURN (uint32_t, std::hash>{} (hb_deref (v))) + + public: + + template constexpr auto + operator () (const T& v) const HB_RETURN (uint32_t, impl (v, hb_prioritize)) +} +HB_FUNCOBJ (hb_hash); + + +struct +{ + private: + + /* Pointer-to-member-function. */ + template auto + impl (Appl&& a, hb_priority<2>, T &&v, Ts&&... ds) const HB_AUTO_RETURN + ((hb_deref (std::forward (v)).*std::forward (a)) (std::forward (ds)...)) + + /* Pointer-to-member. */ + template auto + impl (Appl&& a, hb_priority<1>, T &&v) const HB_AUTO_RETURN + ((hb_deref (std::forward (v))).*std::forward (a)) + + /* Operator(). */ + template auto + impl (Appl&& a, hb_priority<0>, Ts&&... ds) const HB_AUTO_RETURN + (hb_deref (std::forward (a)) (std::forward (ds)...)) + + public: + + template auto + operator () (Appl&& a, Ts&&... ds) const HB_AUTO_RETURN + ( + impl (std::forward (a), + hb_prioritize, + std::forward (ds)...) + ) +} +HB_FUNCOBJ (hb_invoke); + +template +struct hb_partial_t +{ + hb_partial_t (Appl a, V v) : a (a), v (v) {} + + static_assert (Pos > 0, ""); + + template auto + operator () (Ts&& ...ds) -> decltype (hb_invoke (hb_declval (Appl), + hb_declval (V), + hb_declval (Ts)...)) + { + return hb_invoke (std::forward (a), + std::forward (v), + std::forward (ds)...); + } + template auto + operator () (T0&& d0, Ts&& ...ds) -> decltype (hb_invoke (hb_declval (Appl), + hb_declval (T0), + hb_declval (V), + hb_declval (Ts)...)) + { + return hb_invoke (std::forward (a), + std::forward (d0), + std::forward (v), + std::forward (ds)...); + } + + private: + hb_reference_wrapper a; + V v; +}; +template +auto hb_partial (Appl&& a, V&& v) HB_AUTO_RETURN +(( hb_partial_t (a, v) )) + +/* The following, HB_PARTIALIZE, macro uses a particular corner-case + * of C++11 that is not particularly well-supported by all compilers. + * What's happening is that it's using "this" in a trailing return-type + * via decltype(). Broken compilers deduce the type of "this" pointer + * in that context differently from what it resolves to in the body + * of the function. + * + * One probable cause of this is that at the time of trailing return + * type declaration, "this" points to an incomplete type, whereas in + * the function body the type is complete. That doesn't justify the + * error in any way, but is probably what's happening. + * + * In the case of MSVC, we get around this by using C++14 "decltype(auto)" + * which deduces the type from the actual return statement. For gcc 4.8 + * we use "+this" instead of "this" which produces an rvalue that seems + * to be deduced as the same type with this particular compiler, and seem + * to be fine as default code path as well. + */ +#ifdef _MSC_VER +/* https://github.com/harfbuzz/harfbuzz/issues/1730 */ \ +#define HB_PARTIALIZE(Pos) \ + template \ + decltype(auto) operator () (_T&& _v) const \ + { return hb_partial (this, std::forward<_T> (_v)); } \ + static_assert (true, "") +#else +/* https://github.com/harfbuzz/harfbuzz/issues/1724 */ +#define HB_PARTIALIZE(Pos) \ + template \ + auto operator () (_T&& _v) const HB_AUTO_RETURN \ + (hb_partial (+this, std::forward<_T> (_v))) \ + static_assert (true, "") +#endif + + +struct +{ + private: + + template auto + impl (Pred&& p, Val &&v, hb_priority<1>) const HB_AUTO_RETURN + ( + hb_deref (std::forward (p)).has (std::forward (v)) + ) + + template auto + impl (Pred&& p, Val &&v, hb_priority<0>) const HB_AUTO_RETURN + ( + hb_invoke (std::forward (p), + std::forward (v)) + ) + + public: + + template auto + operator () (Pred&& p, Val &&v) const HB_RETURN (bool, + impl (std::forward (p), + std::forward (v), + hb_prioritize) + ) +} +HB_FUNCOBJ (hb_has); + +struct +{ + private: + + template auto + impl (Pred&& p, Val &&v, hb_priority<1>) const HB_AUTO_RETURN + ( + hb_has (std::forward (p), + std::forward (v)) + ) + + template auto + impl (Pred&& p, Val &&v, hb_priority<0>) const HB_AUTO_RETURN + ( + std::forward (p) == std::forward (v) + ) + + public: + + template auto + operator () (Pred&& p, Val &&v) const HB_RETURN (bool, + impl (std::forward (p), + std::forward (v), + hb_prioritize) + ) +} +HB_FUNCOBJ (hb_match); + +struct +{ + private: + + template auto + impl (Proj&& f, Val &&v, hb_priority<2>) const HB_AUTO_RETURN + ( + hb_deref (std::forward (f)).get (std::forward (v)) + ) + + template auto + impl (Proj&& f, Val &&v, hb_priority<1>) const HB_AUTO_RETURN + ( + hb_invoke (std::forward (f), + std::forward (v)) + ) + + template auto + impl (Proj&& f, Val &&v, hb_priority<0>) const HB_AUTO_RETURN + ( + std::forward (f)[std::forward (v)] + ) + + public: + + template auto + operator () (Proj&& f, Val &&v) const HB_AUTO_RETURN + ( + impl (std::forward (f), + std::forward (v), + hb_prioritize) + ) +} +HB_FUNCOBJ (hb_get); + +struct +{ + private: + + template auto + impl (T1&& v1, T2 &&v2, hb_priority<3>) const HB_AUTO_RETURN + ( + std::forward (v2).cmp (std::forward (v1)) == 0 + ) + + template auto + impl (T1&& v1, T2 &&v2, hb_priority<2>) const HB_AUTO_RETURN + ( + std::forward (v1).cmp (std::forward (v2)) == 0 + ) + + template auto + impl (T1&& v1, T2 &&v2, hb_priority<1>) const HB_AUTO_RETURN + ( + std::forward (v1) == std::forward (v2) + ) + + template auto + impl (T1&& v1, T2 &&v2, hb_priority<0>) const HB_AUTO_RETURN + ( + std::forward (v2) == std::forward (v1) + ) + + public: + + template auto + operator () (T1&& v1, T2 &&v2) const HB_AUTO_RETURN + ( + impl (std::forward (v1), + std::forward (v2), + hb_prioritize) + ) +} +HB_FUNCOBJ (hb_equal); + +struct +{ + template void + operator () (T& a, T& b) const + { + using std::swap; // allow ADL + swap (a, b); + } +} +HB_FUNCOBJ (hb_swap); + + +template +struct hb_pair_t +{ + typedef T1 first_t; + typedef T2 second_t; + typedef hb_pair_t pair_t; + + template ::value && + std::is_default_constructible::value)> + hb_pair_t () : first (), second () {} + hb_pair_t (T1 a, T2 b) : first (std::forward (a)), second (std::forward (b)) {} + + template + operator hb_pair_t () { return hb_pair_t (first, second); } + + hb_pair_t reverse () const + { return hb_pair_t (second, first); } + + bool operator == (const pair_t& o) const { return first == o.first && second == o.second; } + bool operator != (const pair_t& o) const { return !(*this == o); } + bool operator < (const pair_t& o) const { return first < o.first || (first == o.first && second < o.second); } + bool operator >= (const pair_t& o) const { return !(*this < o); } + bool operator > (const pair_t& o) const { return first > o.first || (first == o.first && second > o.second); } + bool operator <= (const pair_t& o) const { return !(*this > o); } + + static int cmp (const void *pa, const void *pb) + { + pair_t *a = (pair_t *) pa; + pair_t *b = (pair_t *) pb; + + if (a->first < b->first) return -1; + if (a->first > b->first) return +1; + if (a->second < b->second) return -1; + if (a->second > b->second) return +1; + return 0; + } + + friend void swap (hb_pair_t& a, hb_pair_t& b) + { + hb_swap (a.first, b.first); + hb_swap (a.second, b.second); + } + + + T1 first; + T2 second; +}; +template static inline hb_pair_t +hb_pair (T1&& a, T2&& b) { return hb_pair_t (a, b); } + +typedef hb_pair_t hb_codepoint_pair_t; + +struct +{ + template constexpr typename Pair::first_t + operator () (const Pair& pair) const { return pair.first; } +} +HB_FUNCOBJ (hb_first); + +struct +{ + template constexpr typename Pair::second_t + operator () (const Pair& pair) const { return pair.second; } +} +HB_FUNCOBJ (hb_second); + +/* Note. In min/max impl, we can use hb_type_identity for second argument. + * However, that would silently convert between different-signedness integers. + * Instead we accept two different types, such that compiler can err if + * comparing integers of different signedness. */ +struct +{ + template constexpr auto + operator () (T&& a, T2&& b) const HB_AUTO_RETURN + (a <= b ? a : b) +} +HB_FUNCOBJ (hb_min); +struct +{ + template constexpr auto + operator () (T&& a, T2&& b) const HB_AUTO_RETURN + (a >= b ? a : b) +} +HB_FUNCOBJ (hb_max); +struct +{ + template constexpr auto + operator () (T&& x, T2&& min, T3&& max) const HB_AUTO_RETURN + (hb_min (hb_max (std::forward (x), std::forward (min)), std::forward (max))) +} +HB_FUNCOBJ (hb_clamp); + +/* + * Bithacks. + */ + +/* Return the number of 1 bits in v. */ +template +static inline unsigned int +hb_popcount (T v) +{ +#if hb_has_builtin(__builtin_popcount) + if (sizeof (T) <= sizeof (unsigned int)) + return __builtin_popcount (v); +#endif + +#if hb_has_builtin(__builtin_popcountl) + if (sizeof (T) <= sizeof (unsigned long)) + return __builtin_popcountl (v); +#endif + +#if hb_has_builtin(__builtin_popcountll) + if (sizeof (T) <= sizeof (unsigned long long)) + return __builtin_popcountll (v); +#endif + + if (sizeof (T) <= 4) + { + /* "HACKMEM 169" */ + uint32_t y; + y = (v >> 1) &033333333333; + y = v - y - ((y >>1) & 033333333333); + return (((y + (y >> 3)) & 030707070707) % 077); + } + + if (sizeof (T) == 8) + { + uint64_t y = (uint64_t) v; + y -= ((y >> 1) & 0x5555555555555555ull); + y = (y & 0x3333333333333333ull) + (y >> 2 & 0x3333333333333333ull); + return ((y + (y >> 4)) & 0xf0f0f0f0f0f0f0full) * 0x101010101010101ull >> 56; + } + + if (sizeof (T) == 16) + { + unsigned int shift = 64; + return hb_popcount ((uint64_t) v) + hb_popcount ((uint64_t) (v >> shift)); + } + + assert (0); + return 0; /* Shut up stupid compiler. */ +} + +/* Returns the number of bits needed to store number */ +template +static inline unsigned int +hb_bit_storage (T v) +{ + if (unlikely (!v)) return 0; + +#if hb_has_builtin(__builtin_clz) + if (sizeof (T) <= sizeof (unsigned int)) + return sizeof (unsigned int) * 8 - __builtin_clz (v); +#endif + +#if hb_has_builtin(__builtin_clzl) + if (sizeof (T) <= sizeof (unsigned long)) + return sizeof (unsigned long) * 8 - __builtin_clzl (v); +#endif + +#if hb_has_builtin(__builtin_clzll) + if (sizeof (T) <= sizeof (unsigned long long)) + return sizeof (unsigned long long) * 8 - __builtin_clzll (v); +#endif + +#if (defined(_MSC_VER) && _MSC_VER >= 1500) || (defined(__MINGW32__) && (__GNUC__ < 4)) + if (sizeof (T) <= sizeof (unsigned int)) + { + unsigned long where; + _BitScanReverse (&where, v); + return 1 + where; + } +# if defined(_WIN64) + if (sizeof (T) <= 8) + { + unsigned long where; + _BitScanReverse64 (&where, v); + return 1 + where; + } +# endif +#endif + + if (sizeof (T) <= 4) + { + /* "bithacks" */ + const unsigned int b[] = {0x2, 0xC, 0xF0, 0xFF00, 0xFFFF0000}; + const unsigned int S[] = {1, 2, 4, 8, 16}; + unsigned int r = 0; + for (int i = 4; i >= 0; i--) + if (v & b[i]) + { + v >>= S[i]; + r |= S[i]; + } + return r + 1; + } + if (sizeof (T) <= 8) + { + /* "bithacks" */ + const uint64_t b[] = {0x2ULL, 0xCULL, 0xF0ULL, 0xFF00ULL, 0xFFFF0000ULL, 0xFFFFFFFF00000000ULL}; + const unsigned int S[] = {1, 2, 4, 8, 16, 32}; + unsigned int r = 0; + for (int i = 5; i >= 0; i--) + if (v & b[i]) + { + v >>= S[i]; + r |= S[i]; + } + return r + 1; + } + if (sizeof (T) == 16) + { + unsigned int shift = 64; + return (v >> shift) ? hb_bit_storage ((uint64_t) (v >> shift)) + shift : + hb_bit_storage ((uint64_t) v); + } + + assert (0); + return 0; /* Shut up stupid compiler. */ +} + +/* Returns the number of zero bits in the least significant side of v */ +template +static inline unsigned int +hb_ctz (T v) +{ + if (unlikely (!v)) return 8 * sizeof (T); + +#if hb_has_builtin(__builtin_ctz) + if (sizeof (T) <= sizeof (unsigned int)) + return __builtin_ctz (v); +#endif + +#if hb_has_builtin(__builtin_ctzl) + if (sizeof (T) <= sizeof (unsigned long)) + return __builtin_ctzl (v); +#endif + +#if hb_has_builtin(__builtin_ctzll) + if (sizeof (T) <= sizeof (unsigned long long)) + return __builtin_ctzll (v); +#endif + +#if (defined(_MSC_VER) && _MSC_VER >= 1500) || (defined(__MINGW32__) && (__GNUC__ < 4)) + if (sizeof (T) <= sizeof (unsigned int)) + { + unsigned long where; + _BitScanForward (&where, v); + return where; + } +# if defined(_WIN64) + if (sizeof (T) <= 8) + { + unsigned long where; + _BitScanForward64 (&where, v); + return where; + } +# endif +#endif + + if (sizeof (T) <= 4) + { + /* "bithacks" */ + unsigned int c = 32; + v &= - (int32_t) v; + if (v) c--; + if (v & 0x0000FFFF) c -= 16; + if (v & 0x00FF00FF) c -= 8; + if (v & 0x0F0F0F0F) c -= 4; + if (v & 0x33333333) c -= 2; + if (v & 0x55555555) c -= 1; + return c; + } + if (sizeof (T) <= 8) + { + /* "bithacks" */ + unsigned int c = 64; + v &= - (int64_t) (v); + if (v) c--; + if (v & 0x00000000FFFFFFFFULL) c -= 32; + if (v & 0x0000FFFF0000FFFFULL) c -= 16; + if (v & 0x00FF00FF00FF00FFULL) c -= 8; + if (v & 0x0F0F0F0F0F0F0F0FULL) c -= 4; + if (v & 0x3333333333333333ULL) c -= 2; + if (v & 0x5555555555555555ULL) c -= 1; + return c; + } + if (sizeof (T) == 16) + { + unsigned int shift = 64; + return (uint64_t) v ? hb_bit_storage ((uint64_t) v) : + hb_bit_storage ((uint64_t) (v >> shift)) + shift; + } + + assert (0); + return 0; /* Shut up stupid compiler. */ +} + + +/* + * Tiny stuff. + */ + +/* ASCII tag/character handling */ +static inline bool ISALPHA (unsigned char c) +{ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); } +static inline bool ISALNUM (unsigned char c) +{ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'); } +static inline bool ISSPACE (unsigned char c) +{ return c == ' ' || c =='\f'|| c =='\n'|| c =='\r'|| c =='\t'|| c =='\v'; } +static inline unsigned char TOUPPER (unsigned char c) +{ return (c >= 'a' && c <= 'z') ? c - 'a' + 'A' : c; } +static inline unsigned char TOLOWER (unsigned char c) +{ return (c >= 'A' && c <= 'Z') ? c - 'A' + 'a' : c; } +static inline bool ISHEX (unsigned char c) +{ return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); } +static inline unsigned char TOHEX (uint8_t c) +{ return (c & 0xF) <= 9 ? (c & 0xF) + '0' : (c & 0xF) + 'a' - 10; } +static inline uint8_t FROMHEX (unsigned char c) +{ return (c >= '0' && c <= '9') ? c - '0' : TOLOWER (c) - 'a' + 10; } + +static inline unsigned int DIV_CEIL (const unsigned int a, unsigned int b) +{ return (a + (b - 1)) / b; } + + +#undef ARRAY_LENGTH +template +static inline unsigned int ARRAY_LENGTH (const Type (&)[n]) { return n; } +/* A const version, but does not detect erratically being called on pointers. */ +#define ARRAY_LENGTH_CONST(__array) ((signed int) (sizeof (__array) / sizeof (__array[0]))) + + +static inline void * +hb_memcpy (void *__restrict dst, const void *__restrict src, size_t len) +{ + /* It's illegal to pass 0 as size to memcpy. */ + if (unlikely (!len)) return dst; + return memcpy (dst, src, len); +} + +static inline int +hb_memcmp (const void *a, const void *b, unsigned int len) +{ + /* It's illegal to pass NULL to memcmp(), even if len is zero. + * So, wrap it. + * https://sourceware.org/bugzilla/show_bug.cgi?id=23878 */ + if (unlikely (!len)) return 0; + return memcmp (a, b, len); +} + +static inline void * +hb_memset (void *s, int c, unsigned int n) +{ + /* It's illegal to pass NULL to memset(), even if n is zero. */ + if (unlikely (!n)) return s; + return memset (s, c, n); +} + +static inline unsigned int +hb_ceil_to_4 (unsigned int v) +{ + return ((v - 1) | 3) + 1; +} + +template static inline bool +hb_in_range (T u, T lo, T hi) +{ + static_assert (!std::is_signed::value, ""); + + /* The casts below are important as if T is smaller than int, + * the subtract results will become a signed int! */ + return (T)(u - lo) <= (T)(hi - lo); +} +template static inline bool +hb_in_ranges (T u, T lo1, T hi1) +{ + return hb_in_range (u, lo1, hi1); +} +template static inline bool +hb_in_ranges (T u, T lo1, T hi1, Ts... ds) +{ + return hb_in_range (u, lo1, hi1) || hb_in_ranges (u, ds...); +} + + +/* + * Overflow checking. + */ + +static inline bool +hb_unsigned_mul_overflows (unsigned int count, unsigned int size, unsigned *result = nullptr) +{ +#if hb_has_builtin(__builtin_mul_overflow) + unsigned stack_result; + if (!result) + result = &stack_result; + return __builtin_mul_overflow (count, size, result); +#endif + + if (result) + *result = count * size; + return (size > 0) && (count >= ((unsigned int) -1) / size); +} + + +/* + * Sort and search. + */ + +template +static int +_hb_cmp_method (const void *pkey, const void *pval, Ts... ds) +{ + const K& key = * (const K*) pkey; + const V& val = * (const V*) pval; + + return val.cmp (key, ds...); +} + +template +static inline bool +hb_bsearch_impl (unsigned *pos, /* Out */ + const K& key, + V* base, size_t nmemb, size_t stride, + int (*compar)(const void *_key, const void *_item, Ts... _ds), + Ts... ds) +{ + /* This is our *only* bsearch implementation. */ + + int min = 0, max = (int) nmemb - 1; + while (min <= max) + { + int mid = ((unsigned int) min + (unsigned int) max) / 2; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-align" + V* p = (V*) (((const char *) base) + (mid * stride)); +#pragma GCC diagnostic pop + int c = compar ((const void *) std::addressof (key), (const void *) p, ds...); + if (c < 0) + max = mid - 1; + else if (c > 0) + min = mid + 1; + else + { + *pos = mid; + return true; + } + } + *pos = min; + return false; +} + +template +static inline V* +hb_bsearch (const K& key, V* base, + size_t nmemb, size_t stride = sizeof (V), + int (*compar)(const void *_key, const void *_item) = _hb_cmp_method) +{ + unsigned pos; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-align" + return hb_bsearch_impl (&pos, key, base, nmemb, stride, compar) ? + (V*) (((const char *) base) + (pos * stride)) : nullptr; +#pragma GCC diagnostic pop +} +template +static inline V* +hb_bsearch (const K& key, V* base, + size_t nmemb, size_t stride, + int (*compar)(const void *_key, const void *_item, Ts... _ds), + Ts... ds) +{ + unsigned pos; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-align" + return hb_bsearch_impl (&pos, key, base, nmemb, stride, compar, ds...) ? + (V*) (((const char *) base) + (pos * stride)) : nullptr; +#pragma GCC diagnostic pop +} + + +/* From https://github.com/noporpoise/sort_r + Feb 5, 2019 (c8c65c1e) + Modified to support optional argument using templates */ + +/* Isaac Turner 29 April 2014 Public Domain */ + +/* +hb_qsort function to be exported. +Parameters: + base is the array to be sorted + nel is the number of elements in the array + width is the size in bytes of each element of the array + compar is the comparison function + arg (optional) is a pointer to be passed to the comparison function + +void hb_qsort(void *base, size_t nel, size_t width, + int (*compar)(const void *_a, const void *_b, [void *_arg]), + [void *arg]); +*/ + +#define SORT_R_SWAP(a,b,tmp) ((void) ((tmp) = (a)), (void) ((a) = (b)), (b) = (tmp)) + +/* swap a and b */ +/* a and b must not be equal! */ +static inline void sort_r_swap(char *__restrict a, char *__restrict b, + size_t w) +{ + char tmp, *end = a+w; + for(; a < end; a++, b++) { SORT_R_SWAP(*a, *b, tmp); } +} + +/* swap a, b iff a>b */ +/* a and b must not be equal! */ +/* __restrict is same as restrict but better support on old machines */ +template +static inline int sort_r_cmpswap(char *__restrict a, + char *__restrict b, size_t w, + int (*compar)(const void *_a, + const void *_b, + Ts... _ds), + Ts... ds) +{ + if(compar(a, b, ds...) > 0) { + sort_r_swap(a, b, w); + return 1; + } + return 0; +} + +/* +Swap consecutive blocks of bytes of size na and nb starting at memory addr ptr, +with the smallest swap so that the blocks are in the opposite order. Blocks may +be internally re-ordered e.g. + 12345ab -> ab34512 + 123abc -> abc123 + 12abcde -> deabc12 +*/ +static inline void sort_r_swap_blocks(char *ptr, size_t na, size_t nb) +{ + if(na > 0 && nb > 0) { + if(na > nb) { sort_r_swap(ptr, ptr+na, nb); } + else { sort_r_swap(ptr, ptr+nb, na); } + } +} + +/* Implement recursive quicksort ourselves */ +/* Note: quicksort is not stable, equivalent values may be swapped */ +template +static inline void sort_r_simple(void *base, size_t nel, size_t w, + int (*compar)(const void *_a, + const void *_b, + Ts... _ds), + Ts... ds) +{ + char *b = (char *)base, *end = b + nel*w; + + /* for(size_t i=0; i b && sort_r_cmpswap(pj-w,pj,w,compar,ds...); pj -= w) {} + } + } + else + { + /* nel > 9; Quicksort */ + + int cmp; + char *pl, *ple, *pr, *pre, *pivot; + char *last = b+w*(nel-1), *tmp; + + /* + Use median of second, middle and second-last items as pivot. + First and last may have been swapped with pivot and therefore be extreme + */ + char *l[3]; + l[0] = b + w; + l[1] = b+w*(nel/2); + l[2] = last - w; + + /* printf("pivots: %i, %i, %i\n", *(int*)l[0], *(int*)l[1], *(int*)l[2]); */ + + if(compar(l[0],l[1],ds...) > 0) { SORT_R_SWAP(l[0], l[1], tmp); } + if(compar(l[1],l[2],ds...) > 0) { + SORT_R_SWAP(l[1], l[2], tmp); + if(compar(l[0],l[1],ds...) > 0) { SORT_R_SWAP(l[0], l[1], tmp); } + } + + /* swap mid value (l[1]), and last element to put pivot as last element */ + if(l[1] != last) { sort_r_swap(l[1], last, w); } + + /* + pl is the next item on the left to be compared to the pivot + pr is the last item on the right that was compared to the pivot + ple is the left position to put the next item that equals the pivot + ple is the last right position where we put an item that equals the pivot + v- end (beyond the array) + EEEEEELLLLLLLLuuuuuuuuGGGGGGGEEEEEEEE. + ^- b ^- ple ^- pl ^- pr ^- pre ^- last (where the pivot is) + Pivot comparison key: + E = equal, L = less than, u = unknown, G = greater than, E = equal + */ + pivot = last; + ple = pl = b; + pre = pr = last; + + /* + Strategy: + Loop into the list from the left and right at the same time to find: + - an item on the left that is greater than the pivot + - an item on the right that is less than the pivot + Once found, they are swapped and the loop continues. + Meanwhile items that are equal to the pivot are moved to the edges of the + array. + */ + while(pl < pr) { + /* Move left hand items which are equal to the pivot to the far left. + break when we find an item that is greater than the pivot */ + for(; pl < pr; pl += w) { + cmp = compar(pl, pivot, ds...); + if(cmp > 0) { break; } + else if(cmp == 0) { + if(ple < pl) { sort_r_swap(ple, pl, w); } + ple += w; + } + } + /* break if last batch of left hand items were equal to pivot */ + if(pl >= pr) { break; } + /* Move right hand items which are equal to the pivot to the far right. + break when we find an item that is less than the pivot */ + for(; pl < pr; ) { + pr -= w; /* Move right pointer onto an unprocessed item */ + cmp = compar(pr, pivot, ds...); + if(cmp == 0) { + pre -= w; + if(pr < pre) { sort_r_swap(pr, pre, w); } + } + else if(cmp < 0) { + if(pl < pr) { sort_r_swap(pl, pr, w); } + pl += w; + break; + } + } + } + + pl = pr; /* pr may have gone below pl */ + + /* + Now we need to go from: EEELLLGGGGEEEE + to: LLLEEEEEEEGGGG + Pivot comparison key: + E = equal, L = less than, u = unknown, G = greater than, E = equal + */ + sort_r_swap_blocks(b, ple-b, pl-ple); + sort_r_swap_blocks(pr, pre-pr, end-pre); + + /*for(size_t i=0; i static inline void +hb_stable_sort (T *array, unsigned int len, int(*compar)(const T2 *, const T2 *), T3 *array2 = nullptr) +{ + static_assert (hb_is_trivially_copy_assignable (T), ""); + static_assert (hb_is_trivially_copy_assignable (T3), ""); + + for (unsigned int i = 1; i < len; i++) + { + unsigned int j = i; + while (j && compar (&array[j - 1], &array[i]) > 0) + j--; + if (i == j) + continue; + /* Move item i to occupy place for item j, shift what's in between. */ + { + T t = array[i]; + memmove (&array[j + 1], &array[j], (i - j) * sizeof (T)); + array[j] = t; + } + if (array2) + { + T3 t = array2[i]; + memmove (&array2[j + 1], &array2[j], (i - j) * sizeof (T3)); + array2[j] = t; + } + } +} + +static inline hb_bool_t +hb_codepoint_parse (const char *s, unsigned int len, int base, hb_codepoint_t *out) +{ + unsigned int v; + const char *p = s; + const char *end = p + len; + if (unlikely (!hb_parse_uint (&p, end, &v, true/* whole buffer */, base))) + return false; + + *out = v; + return true; +} + + +/* Operators. */ + +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T &b) const HB_AUTO_RETURN (a & b) +} +HB_FUNCOBJ (hb_bitwise_and); +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T &b) const HB_AUTO_RETURN (a | b) +} +HB_FUNCOBJ (hb_bitwise_or); +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T &b) const HB_AUTO_RETURN (a ^ b) +} +HB_FUNCOBJ (hb_bitwise_xor); +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T &b) const HB_AUTO_RETURN (~a & b) +} +HB_FUNCOBJ (hb_bitwise_lt); +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T &b) const HB_AUTO_RETURN (a & ~b) +} +HB_FUNCOBJ (hb_bitwise_gt); // aka sub +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T &b) const HB_AUTO_RETURN (~a | b) +} +HB_FUNCOBJ (hb_bitwise_le); +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T &b) const HB_AUTO_RETURN (a | ~b) +} +HB_FUNCOBJ (hb_bitwise_ge); +struct +{ + template constexpr auto + operator () (const T &a) const HB_AUTO_RETURN (~a) +} +HB_FUNCOBJ (hb_bitwise_neg); + +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a + b) +} +HB_FUNCOBJ (hb_add); +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a - b) +} +HB_FUNCOBJ (hb_sub); +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (b - a) +} +HB_FUNCOBJ (hb_rsub); +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a * b) +} +HB_FUNCOBJ (hb_mul); +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a / b) +} +HB_FUNCOBJ (hb_div); +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a % b) +} +HB_FUNCOBJ (hb_mod); +struct +{ + template constexpr auto + operator () (const T &a) const HB_AUTO_RETURN (+a) +} +HB_FUNCOBJ (hb_pos); +struct +{ + template constexpr auto + operator () (const T &a) const HB_AUTO_RETURN (-a) +} +HB_FUNCOBJ (hb_neg); +struct +{ + template constexpr auto + operator () (T &a) const HB_AUTO_RETURN (++a) +} +HB_FUNCOBJ (hb_inc); +struct +{ + template constexpr auto + operator () (T &a) const HB_AUTO_RETURN (--a) +} +HB_FUNCOBJ (hb_dec); + + +/* Adapted from kurbo implementation with extra parameters added, + * and finding for a particular range instead of 0. + * + * For documentation and implementation see: + * + * [ITP method]: https://en.wikipedia.org/wiki/ITP_Method + * [An Enhancement of the Bisection Method Average Performance Preserving Minmax Optimality]: https://dl.acm.org/doi/10.1145/3423597 + * https://docs.rs/kurbo/0.8.1/kurbo/common/fn.solve_itp.html + * https://github.com/linebender/kurbo/blob/fd839c25ea0c98576c7ce5789305822675a89938/src/common.rs#L162-L248 + */ +template +double solve_itp (func_t f, + double a, double b, + double epsilon, + double min_y, double max_y, + double &ya, double &yb, double &y) +{ + unsigned n1_2 = (unsigned) (hb_max (ceil (log2 ((b - a) / epsilon)) - 1.0, 0.0)); + const unsigned n0 = 1; // Hardwired + const double k1 = 0.2 / (b - a); // Hardwired. + unsigned nmax = n0 + n1_2; + double scaled_epsilon = epsilon * double (1llu << nmax); + double _2_epsilon = 2.0 * epsilon; + while (b - a > _2_epsilon) + { + double x1_2 = 0.5 * (a + b); + double r = scaled_epsilon - 0.5 * (b - a); + double xf = (yb * a - ya * b) / (yb - ya); + double sigma = x1_2 - xf; + double b_a = b - a; + // This has k2 = 2 hardwired for efficiency. + double b_a_k2 = b_a * b_a; + double delta = k1 * b_a_k2; + int sigma_sign = sigma >= 0 ? +1 : -1; + double xt = delta <= fabs (x1_2 - xf) ? xf + delta * sigma_sign : x1_2; + double xitp = fabs (xt - x1_2) <= r ? xt : x1_2 - r * sigma_sign; + double yitp = f (xitp); + if (yitp > max_y) + { + b = xitp; + yb = yitp; + } + else if (yitp < min_y) + { + a = xitp; + ya = yitp; + } + else + { + y = yitp; + return xitp; + } + scaled_epsilon *= 0.5; + } + return 0.5 * (a + b); +} + + +#endif /* HB_ALGS_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-array.hh b/src/3rdparty/harfbuzz-ng/src/hb-array.hh new file mode 100644 index 00000000000..760f90259c8 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-array.hh @@ -0,0 +1,502 @@ +/* + * 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): Behdad Esfahbod + */ + +#ifndef HB_ARRAY_HH +#define HB_ARRAY_HH + +#include "hb.hh" +#include "hb-algs.hh" +#include "hb-iter.hh" +#include "hb-null.hh" + + +template +struct hb_sorted_array_t; + +enum hb_not_found_t +{ + HB_NOT_FOUND_DONT_STORE, + HB_NOT_FOUND_STORE, + HB_NOT_FOUND_STORE_CLOSEST, +}; + + +template +struct hb_array_t : hb_iter_with_fallback_t, Type&> +{ + /* + * Constructors. + */ + hb_array_t () = default; + hb_array_t (const hb_array_t&) = default; + ~hb_array_t () = default; + hb_array_t& operator= (const hb_array_t&) = default; + hb_array_t& operator= (hb_array_t&&) = default; + + constexpr hb_array_t (Type *array_, unsigned int length_) : arrayZ (array_), length (length_) {} + template + constexpr hb_array_t (Type (&array_)[length_]) : hb_array_t (array_, length_) {} + + template + constexpr hb_array_t (const hb_array_t &o) : + hb_iter_with_fallback_t (), + arrayZ (o.arrayZ), length (o.length), backwards_length (o.backwards_length) {} + template + hb_array_t& operator = (const hb_array_t &o) + { arrayZ = o.arrayZ; length = o.length; backwards_length = o.backwards_length; return *this; } + + /* + * Iterator implementation. + */ + typedef Type& __item_t__; + static constexpr bool is_random_access_iterator = true; + static constexpr bool has_fast_len = true; + Type& __item__ () const + { + if (unlikely (!length)) return CrapOrNull (Type); + return *arrayZ; + } + Type& __item_at__ (unsigned i) const + { + if (unlikely (i >= length)) return CrapOrNull (Type); + return arrayZ[i]; + } + void __next__ () + { + if (unlikely (!length)) + return; + length--; + backwards_length++; + arrayZ++; + } + void __forward__ (unsigned n) + { + if (unlikely (n > length)) + n = length; + length -= n; + backwards_length += n; + arrayZ += n; + } + void __prev__ () + { + if (unlikely (!backwards_length)) + return; + length++; + backwards_length--; + arrayZ--; + } + void __rewind__ (unsigned n) + { + if (unlikely (n > backwards_length)) + n = backwards_length; + length += n; + backwards_length -= n; + arrayZ -= n; + } + unsigned __len__ () const { return length; } + /* Ouch. The operator== compares the contents of the array. For range-based for loops, + * it's best if we can just compare arrayZ, though comparing contents is still fast, + * but also would require that Type has operator==. As such, we optimize this operator + * for range-based for loop and just compare arrayZ and length. + * + * The above comment is outdated now because we implemented separate begin/end to + * objects that were using hb_array_t for range-based loop before. */ + bool operator != (const hb_array_t& o) const + { return this->arrayZ != o.arrayZ || this->length != o.length; } + + /* Faster range-based for loop without bounds-check. */ + Type *begin () const { return arrayZ; } + Type *end () const { return arrayZ + length; } + + + /* Extra operators. + */ + Type * operator & () const { return arrayZ; } + operator hb_array_t () { return hb_array_t (arrayZ, length); } + template operator T * () const { return arrayZ; } + + HB_INTERNAL bool operator == (const hb_array_t &o) const; + + uint32_t hash () const + { + // FNV-1a hash function + // https://github.com/harfbuzz/harfbuzz/pull/4228 + uint32_t current = /*cbf29ce4*/0x84222325; + for (auto &v : *this) + { + current = current ^ hb_hash (v); + current = current * 16777619; + } + return current; + } + + /* + * Compare, Sort, and Search. + */ + + /* Note: our compare is NOT lexicographic; it also does NOT call Type::cmp. */ + int cmp (const hb_array_t &a) const + { + if (length != a.length) + return (int) a.length - (int) length; + return hb_memcmp (a.arrayZ, arrayZ, get_size ()); + } + HB_INTERNAL static int cmp (const void *pa, const void *pb) + { + hb_array_t *a = (hb_array_t *) pa; + hb_array_t *b = (hb_array_t *) pb; + return b->cmp (*a); + } + + template + Type *lsearch (const T &x, Type *not_found = nullptr) + { + unsigned i; + return lfind (x, &i) ? &this->arrayZ[i] : not_found; + } + template + const Type *lsearch (const T &x, const Type *not_found = nullptr) const + { + unsigned i; + return lfind (x, &i) ? &this->arrayZ[i] : not_found; + } + template + bool lfind (const T &x, unsigned *pos = nullptr, + hb_not_found_t not_found = HB_NOT_FOUND_DONT_STORE, + unsigned int to_store = (unsigned int) -1) const + { + for (unsigned i = 0; i < length; ++i) + if (hb_equal (x, this->arrayZ[i])) + { + if (pos) + *pos = i; + return true; + } + + if (pos) + { + switch (not_found) + { + case HB_NOT_FOUND_DONT_STORE: + break; + + case HB_NOT_FOUND_STORE: + *pos = to_store; + break; + + case HB_NOT_FOUND_STORE_CLOSEST: + *pos = length; + break; + } + } + return false; + } + + hb_sorted_array_t qsort (int (*cmp_)(const void*, const void*)) + { + //static_assert (hb_enable_if (hb_is_trivially_copy_assignable(Type)), ""); + if (likely (length)) + hb_qsort (arrayZ, length, this->get_item_size (), cmp_); + return hb_sorted_array_t (*this); + } + hb_sorted_array_t qsort () + { + //static_assert (hb_enable_if (hb_is_trivially_copy_assignable(Type)), ""); + if (likely (length)) + hb_qsort (arrayZ, length, this->get_item_size (), Type::cmp); + return hb_sorted_array_t (*this); + } + + /* + * Other methods. + */ + + unsigned int get_size () const { return length * this->get_item_size (); } + + /* + * Reverse the order of items in this array in the range [start, end). + */ + void reverse (unsigned start = 0, unsigned end = -1) + { + start = hb_min (start, length); + end = hb_min (end, length); + + if (end < start + 2) + return; + + for (unsigned lhs = start, rhs = end - 1; lhs < rhs; lhs++, rhs--) + hb_swap (arrayZ[rhs], arrayZ[lhs]); + } + + hb_array_t sub_array (unsigned int start_offset = 0, unsigned int *seg_count = nullptr /* IN/OUT */) const + { + if (!start_offset && !seg_count) + return *this; + + unsigned int count = length; + if (unlikely (start_offset > count)) + count = 0; + else + count -= start_offset; + if (seg_count) + count = *seg_count = hb_min (count, *seg_count); + return hb_array_t (arrayZ + start_offset, count); + } + hb_array_t sub_array (unsigned int start_offset, unsigned int seg_count) const + { return sub_array (start_offset, &seg_count); } + + hb_array_t truncate (unsigned length) const { return sub_array (0, length); } + + template + const T *as () const + { return length < hb_min_size (T) ? &Null (T) : reinterpret_cast (arrayZ); } + + template + bool check_range (const T *p, unsigned int size = T::static_size) const + { + return arrayZ <= ((const char *) p) + && ((const char *) p) <= arrayZ + length + && (unsigned int) (arrayZ + length - (const char *) p) >= size; + } + + /* Only call if you allocated the underlying array using hb_malloc() or similar. */ + void fini () + { hb_free ((void *) arrayZ); arrayZ = nullptr; length = 0; } + + template )))> + hb_array_t copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + auto* out = c->start_embed (arrayZ); + if (unlikely (!c->extend_size (out, get_size (), false))) return_trace (hb_array_t ()); + for (unsigned i = 0; i < length; i++) + out[i] = arrayZ[i]; /* TODO: add version that calls c->copy() */ + return_trace (hb_array_t (out, length)); + } + + template ))> + hb_array_t copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + auto* out = c->start_embed (arrayZ); + if (unlikely (!c->extend_size (out, get_size (), false))) return_trace (hb_array_t ()); + hb_memcpy (out, arrayZ, get_size ()); + return_trace (hb_array_t (out, length)); + } + + template + bool sanitize (hb_sanitize_context_t *c) const + { return c->check_array (arrayZ, length); } + + /* + * Members + */ + + public: + Type *arrayZ = nullptr; + unsigned int length = 0; + unsigned int backwards_length = 0; +}; +template inline hb_array_t +hb_array () +{ return hb_array_t (); } +template inline hb_array_t +hb_array (T *array, unsigned int length) +{ return hb_array_t (array, length); } +template inline hb_array_t +hb_array (T (&array_)[length_]) +{ return hb_array_t (array_); } + +template +struct hb_sorted_array_t : + hb_array_t, + hb_iter_t, Type&> +{ + typedef hb_iter_t iter_base_t; + HB_ITER_USING (iter_base_t); + static constexpr bool is_random_access_iterator = true; + static constexpr bool is_sorted_iterator = true; + static constexpr bool has_fast_len = true; + + hb_sorted_array_t () = default; + hb_sorted_array_t (const hb_sorted_array_t&) = default; + ~hb_sorted_array_t () = default; + hb_sorted_array_t& operator= (const hb_sorted_array_t&) = default; + hb_sorted_array_t& operator= (hb_sorted_array_t&&) = default; + + constexpr hb_sorted_array_t (Type *array_, unsigned int length_) : hb_array_t (array_, length_) {} + template + constexpr hb_sorted_array_t (Type (&array_)[length_]) : hb_array_t (array_) {} + + template + constexpr hb_sorted_array_t (const hb_array_t &o) : + hb_array_t (o), + hb_iter_t () {} + template + hb_sorted_array_t& operator = (const hb_array_t &o) + { hb_array_t (*this) = o; return *this; } + + /* Iterator implementation. */ + + /* See comment in hb_array_of::operator != */ + bool operator != (const hb_sorted_array_t& o) const + { return this->arrayZ != o.arrayZ || this->length != o.length; } + + /* Faster range-based for loop without bounds-check. */ + Type *begin () const { return this->arrayZ; } + Type *end () const { return this->arrayZ + this->length; } + + + hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int *seg_count /* IN/OUT */) const + { return hb_sorted_array_t (((const hb_array_t *) (this))->sub_array (start_offset, seg_count)); } + hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int seg_count) const + { return sub_array (start_offset, &seg_count); } + + hb_sorted_array_t truncate (unsigned length) const { return sub_array (0, length); } + + template + Type *bsearch (const T &x, Type *not_found = nullptr) + { + unsigned int i; + return bfind (x, &i) ? &this->arrayZ[i] : not_found; + } + template + const Type *bsearch (const T &x, const Type *not_found = nullptr) const + { + unsigned int i; + return bfind (x, &i) ? &this->arrayZ[i] : not_found; + } + template + bool bfind (const T &x, unsigned int *i = nullptr, + hb_not_found_t not_found = HB_NOT_FOUND_DONT_STORE, + unsigned int to_store = (unsigned int) -1) const + { + unsigned pos; + + if (bsearch_impl (x, &pos)) + { + if (i) + *i = pos; + return true; + } + + if (i) + { + switch (not_found) + { + case HB_NOT_FOUND_DONT_STORE: + break; + + case HB_NOT_FOUND_STORE: + *i = to_store; + break; + + case HB_NOT_FOUND_STORE_CLOSEST: + *i = pos; + break; + } + } + return false; + } + template + bool bsearch_impl (const T &x, unsigned *pos, Ts... ds) const + { + return hb_bsearch_impl (pos, + x, + this->arrayZ, + this->length, + sizeof (Type), + _hb_cmp_method, + std::forward (ds)...); + } +}; +template inline hb_sorted_array_t +hb_sorted_array (T *array, unsigned int length) +{ return hb_sorted_array_t (array, length); } +template inline hb_sorted_array_t +hb_sorted_array (T (&array_)[length_]) +{ return hb_sorted_array_t (array_); } + +template +inline bool hb_array_t::operator == (const hb_array_t &o) const +{ + if (o.length != this->length) return false; + for (unsigned int i = 0; i < this->length; i++) { + if (this->arrayZ[i] != o.arrayZ[i]) return false; + } + return true; +} +template <> +inline bool hb_array_t::operator == (const hb_array_t &o) const +{ + if (o.length != this->length) return false; + return 0 == hb_memcmp (arrayZ, o.arrayZ, length); +} +template <> +inline bool hb_array_t::operator == (const hb_array_t &o) const +{ + if (o.length != this->length) return false; + return 0 == hb_memcmp (arrayZ, o.arrayZ, length); +} + + +/* Specialize hash() for byte arrays. */ + +#ifndef HB_OPTIMIZE_SIZE_MORE +template <> +inline uint32_t hb_array_t::hash () const +{ + // https://github.com/harfbuzz/harfbuzz/pull/4228 + return fasthash32(arrayZ, length, 0xf437ffe6 /* magic? */); +} + +template <> +inline uint32_t hb_array_t::hash () const +{ + // https://github.com/harfbuzz/harfbuzz/pull/4228 + return fasthash32(arrayZ, length, 0xf437ffe6 /* magic? */); +} +#endif + + +typedef hb_array_t hb_bytes_t; +typedef hb_array_t hb_ubytes_t; + + + +#endif /* HB_ARRAY_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-atomic-private.hh b/src/3rdparty/harfbuzz-ng/src/hb-atomic-private.hh deleted file mode 100644 index 93438401400..00000000000 --- a/src/3rdparty/harfbuzz-ng/src/hb-atomic-private.hh +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright © 2007 Chris Wilson - * Copyright © 2009,2010 Red Hat, Inc. - * Copyright © 2011,2012 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. - * - * Contributor(s): - * Chris Wilson - * Red Hat Author(s): Behdad Esfahbod - * Google Author(s): Behdad Esfahbod - */ - -#ifndef HB_ATOMIC_PRIVATE_HH -#define HB_ATOMIC_PRIVATE_HH - -#include "hb-private.hh" - - -/* atomic_int */ - -/* We need external help for these */ - -#if defined(hb_atomic_int_impl_add) \ - && defined(hb_atomic_ptr_impl_get) \ - && defined(hb_atomic_ptr_impl_cmpexch) - -/* Defined externally, i.e. in config.h; must have typedef'ed hb_atomic_int_impl_t as well. */ - - -#elif !defined(HB_NO_MT) && (defined(_WIN32) || defined(__CYGWIN__)) - -#include - -/* MinGW has a convoluted history of supporting MemoryBarrier - * properly. As such, define a function to wrap the whole - * thing. */ -static inline void _HBMemoryBarrier (void) { -#if !defined(MemoryBarrier) - long dummy = 0; - InterlockedExchange (&dummy, 1); -#else - MemoryBarrier (); -#endif -} - -typedef LONG hb_atomic_int_impl_t; -#define HB_ATOMIC_INT_IMPL_INIT(V) (V) -#define hb_atomic_int_impl_add(AI, V) InterlockedExchangeAdd (&(AI), (V)) - -#define hb_atomic_ptr_impl_get(P) (_HBMemoryBarrier (), (void *) *(P)) -#define hb_atomic_ptr_impl_cmpexch(P,O,N) (InterlockedCompareExchangePointer ((void **) (P), (void *) (N), (void *) (O)) == (void *) (O)) - - -#elif !defined(HB_NO_MT) && defined(__APPLE__) - -#include -#ifdef __MAC_OS_X_MIN_REQUIRED -#include -#elif defined(__IPHONE_OS_MIN_REQUIRED) -#include -#endif - - -typedef int32_t hb_atomic_int_impl_t; -#define HB_ATOMIC_INT_IMPL_INIT(V) (V) -#define hb_atomic_int_impl_add(AI, V) (OSAtomicAdd32Barrier ((V), &(AI)) - (V)) - -#define hb_atomic_ptr_impl_get(P) (OSMemoryBarrier (), (void *) *(P)) -#if (MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 || __IPHONE_VERSION_MIN_REQUIRED >= 20100) -#define hb_atomic_ptr_impl_cmpexch(P,O,N) OSAtomicCompareAndSwapPtrBarrier ((void *) (O), (void *) (N), (void **) (P)) -#else -#if __ppc64__ || __x86_64__ || __aarch64__ -#define hb_atomic_ptr_impl_cmpexch(P,O,N) OSAtomicCompareAndSwap64Barrier ((int64_t) (void *) (O), (int64_t) (void *) (N), (int64_t*) (P)) -#else -#define hb_atomic_ptr_impl_cmpexch(P,O,N) OSAtomicCompareAndSwap32Barrier ((int32_t) (void *) (O), (int32_t) (void *) (N), (int32_t*) (P)) -#endif -#endif - - -#elif !defined(HB_NO_MT) && defined(HAVE_INTEL_ATOMIC_PRIMITIVES) - -typedef int hb_atomic_int_impl_t; -#define HB_ATOMIC_INT_IMPL_INIT(V) (V) -#define hb_atomic_int_impl_add(AI, V) __sync_fetch_and_add (&(AI), (V)) - -#define hb_atomic_ptr_impl_get(P) (void *) (__sync_synchronize (), *(P)) -#define hb_atomic_ptr_impl_cmpexch(P,O,N) __sync_bool_compare_and_swap ((P), (O), (N)) - - -#elif !defined(HB_NO_MT) && defined(HAVE_SOLARIS_ATOMIC_OPS) - -#include -#include - -typedef unsigned int hb_atomic_int_impl_t; -#define HB_ATOMIC_INT_IMPL_INIT(V) (V) -#define hb_atomic_int_impl_add(AI, V) ( ({__machine_rw_barrier ();}), atomic_add_int_nv (&(AI), (V)) - (V)) - -#define hb_atomic_ptr_impl_get(P) ( ({__machine_rw_barrier ();}), (void *) *(P)) -#define hb_atomic_ptr_impl_cmpexch(P,O,N) ( ({__machine_rw_barrier ();}), atomic_cas_ptr ((void **) (P), (void *) (O), (void *) (N)) == (void *) (O) ? true : false) - - -#elif !defined(HB_NO_MT) && defined(_AIX) && defined(__IBMCPP__) - -#include - - -static inline int _hb_fetch_and_add(volatile int* AI, unsigned int V) { - __lwsync(); - int result = __fetch_and_add(AI, V); - __isync(); - return result; -} -static inline int _hb_compare_and_swaplp(volatile long* P, long O, long N) { - __sync(); - int result = __compare_and_swaplp (P, &O, N); - __sync(); - return result; -} - -typedef int hb_atomic_int_impl_t; -#define HB_ATOMIC_INT_IMPL_INIT(V) (V) -#define hb_atomic_int_impl_add(AI, V) _hb_fetch_and_add (&(AI), (V)) - -#define hb_atomic_ptr_impl_get(P) (__sync(), (void *) *(P)) -#define hb_atomic_ptr_impl_cmpexch(P,O,N) _hb_compare_and_swaplp ((long*)(P), (long)(O), (long)(N)) - -#elif !defined(HB_NO_MT) - -#define HB_ATOMIC_INT_NIL 1 /* Warn that fallback implementation is in use. */ - -typedef volatile int hb_atomic_int_impl_t; -#define HB_ATOMIC_INT_IMPL_INIT(V) (V) -#define hb_atomic_int_impl_add(AI, V) (((AI) += (V)) - (V)) - -#define hb_atomic_ptr_impl_get(P) ((void *) *(P)) -#define hb_atomic_ptr_impl_cmpexch(P,O,N) (* (void * volatile *) (P) == (void *) (O) ? (* (void * volatile *) (P) = (void *) (N), true) : false) - - -#else /* HB_NO_MT */ - -typedef int hb_atomic_int_impl_t; -#define HB_ATOMIC_INT_IMPL_INIT(V) (V) -#define hb_atomic_int_impl_add(AI, V) (((AI) += (V)) - (V)) - -#define hb_atomic_ptr_impl_get(P) ((void *) *(P)) -#define hb_atomic_ptr_impl_cmpexch(P,O,N) (* (void **) (P) == (void *) (O) ? (* (void **) (P) = (void *) (N), true) : false) - - -#endif - - -#define HB_ATOMIC_INT_INIT(V) {HB_ATOMIC_INT_IMPL_INIT(V)} - -struct hb_atomic_int_t -{ - hb_atomic_int_impl_t v; - - inline void set_unsafe (int v_) { v = v_; } - inline int get_unsafe (void) const { return v; } - inline int inc (void) { return hb_atomic_int_impl_add (const_cast (v), 1); } - inline int dec (void) { return hb_atomic_int_impl_add (const_cast (v), -1); } -}; - - -#define hb_atomic_ptr_get(P) hb_atomic_ptr_impl_get(P) -#define hb_atomic_ptr_cmpexch(P,O,N) hb_atomic_ptr_impl_cmpexch((P),(O),(N)) - - -#endif /* HB_ATOMIC_PRIVATE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-atomic.hh b/src/3rdparty/harfbuzz-ng/src/hb-atomic.hh new file mode 100644 index 00000000000..303dfe6d047 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-atomic.hh @@ -0,0 +1,222 @@ +/* + * Copyright © 2007 Chris Wilson + * Copyright © 2009,2010 Red Hat, Inc. + * Copyright © 2011,2012 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. + * + * Contributor(s): + * Chris Wilson + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_ATOMIC_HH +#define HB_ATOMIC_HH + +#include "hb.hh" +#include "hb-meta.hh" + + +/* + * Atomic integers and pointers. + */ + + +/* We need external help for these */ + +#if defined(hb_atomic_int_impl_add) \ + && defined(hb_atomic_ptr_impl_get) \ + && defined(hb_atomic_ptr_impl_cmpexch) + +/* Defined externally, i.e. in config.h. */ + + +#elif !defined(HB_NO_MT) && defined(__ATOMIC_ACQUIRE) + +/* C++11-style GCC primitives. We prefer these as they don't require linking to libstdc++ / libc++. */ + +#define _hb_memory_barrier() __sync_synchronize () + +#define hb_atomic_int_impl_add(AI, V) __atomic_fetch_add ((AI), (V), __ATOMIC_ACQ_REL) +#define hb_atomic_int_impl_set_relaxed(AI, V) __atomic_store_n ((AI), (V), __ATOMIC_RELAXED) +#define hb_atomic_int_impl_set(AI, V) __atomic_store_n ((AI), (V), __ATOMIC_RELEASE) +#define hb_atomic_int_impl_get_relaxed(AI) __atomic_load_n ((AI), __ATOMIC_RELAXED) +#define hb_atomic_int_impl_get(AI) __atomic_load_n ((AI), __ATOMIC_ACQUIRE) + +#define hb_atomic_ptr_impl_set_relaxed(P, V) __atomic_store_n ((P), (V), __ATOMIC_RELAXED) +#define hb_atomic_ptr_impl_get_relaxed(P) __atomic_load_n ((P), __ATOMIC_RELAXED) +#define hb_atomic_ptr_impl_get(P) __atomic_load_n ((P), __ATOMIC_ACQUIRE) +static inline bool +_hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N) +{ + const void *O = O_; // Need lvalue + return __atomic_compare_exchange_n ((void **) P, (void **) &O, (void *) N, true, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED); +} +#define hb_atomic_ptr_impl_cmpexch(P,O,N) _hb_atomic_ptr_impl_cmplexch ((const void **) (P), (O), (N)) + + +#elif !defined(HB_NO_MT) + +/* C++11 atomics. */ + +#include + +#define _hb_memory_barrier() std::atomic_thread_fence(std::memory_order_ack_rel) +#define _hb_memory_r_barrier() std::atomic_thread_fence(std::memory_order_acquire) +#define _hb_memory_w_barrier() std::atomic_thread_fence(std::memory_order_release) + +#define hb_atomic_int_impl_add(AI, V) (reinterpret_cast::type> *> (AI)->fetch_add ((V), std::memory_order_acq_rel)) +#define hb_atomic_int_impl_set_relaxed(AI, V) (reinterpret_cast::type> *> (AI)->store ((V), std::memory_order_relaxed)) +#define hb_atomic_int_impl_set(AI, V) (reinterpret_cast::type> *> (AI)->store ((V), std::memory_order_release)) +#define hb_atomic_int_impl_get_relaxed(AI) (reinterpret_cast::type> const *> (AI)->load (std::memory_order_relaxed)) +#define hb_atomic_int_impl_get(AI) (reinterpret_cast::type> const *> (AI)->load (std::memory_order_acquire)) + +#define hb_atomic_ptr_impl_set_relaxed(P, V) (reinterpret_cast *> (P)->store ((V), std::memory_order_relaxed)) +#define hb_atomic_ptr_impl_get_relaxed(P) (reinterpret_cast const *> (P)->load (std::memory_order_relaxed)) +#define hb_atomic_ptr_impl_get(P) (reinterpret_cast *> (P)->load (std::memory_order_acquire)) +static inline bool +_hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N) +{ + const void *O = O_; // Need lvalue + return reinterpret_cast *> (P)->compare_exchange_weak (O, N, std::memory_order_acq_rel, std::memory_order_relaxed); +} +#define hb_atomic_ptr_impl_cmpexch(P,O,N) _hb_atomic_ptr_impl_cmplexch ((const void **) (P), (O), (N)) + + +#else /* defined(HB_NO_MT) */ + +#define hb_atomic_int_impl_add(AI, V) ((*(AI) += (V)) - (V)) +#define _hb_memory_barrier() do {} while (0) +#define hb_atomic_ptr_impl_cmpexch(P,O,N) (* (void **) (P) == (void *) (O) ? (* (void **) (P) = (void *) (N), true) : false) + +#endif + + +/* This should never be disabled, even under HB_NO_MT. + * except that MSVC gives me an internal compiler error, so disabled there. + * + * https://github.com/harfbuzz/harfbuzz/pull/4119 + */ +#ifndef _hb_compiler_memory_r_barrier +#if defined(__ATOMIC_ACQUIRE) // gcc-like +#define _hb_compiler_memory_r_barrier() asm volatile("": : :"memory") +#elif !defined(_MSC_VER) +#include +#define _hb_compiler_memory_r_barrier() std::atomic_signal_fence (std::memory_order_acquire) +#else +#define _hb_compiler_memory_r_barrier() do {} while (0) +#endif +#endif + + + +#ifndef _hb_memory_r_barrier +#define _hb_memory_r_barrier() _hb_memory_barrier () +#endif +#ifndef _hb_memory_w_barrier +#define _hb_memory_w_barrier() _hb_memory_barrier () +#endif +#ifndef hb_atomic_int_impl_set_relaxed +#define hb_atomic_int_impl_set_relaxed(AI, V) (*(AI) = (V)) +#endif +#ifndef hb_atomic_int_impl_get_relaxed +#define hb_atomic_int_impl_get_relaxed(AI) (*(AI)) +#endif + +#ifndef hb_atomic_ptr_impl_set_relaxed +#define hb_atomic_ptr_impl_set_relaxed(P, V) (*(P) = (V)) +#endif +#ifndef hb_atomic_ptr_impl_get_relaxed +#define hb_atomic_ptr_impl_get_relaxed(P) (*(P)) +#endif +#ifndef hb_atomic_int_impl_set +inline void hb_atomic_int_impl_set (int *AI, int v) { _hb_memory_w_barrier (); *AI = v; } +inline void hb_atomic_int_impl_set (short *AI, short v) { _hb_memory_w_barrier (); *AI = v; } +#endif +#ifndef hb_atomic_int_impl_get +inline int hb_atomic_int_impl_get (const int *AI) { int v = *AI; _hb_memory_r_barrier (); return v; } +inline short hb_atomic_int_impl_get (const short *AI) { short v = *AI; _hb_memory_r_barrier (); return v; } +#endif +#ifndef hb_atomic_ptr_impl_get +inline void *hb_atomic_ptr_impl_get (void ** const P) { void *v = *P; _hb_memory_r_barrier (); return v; } +#endif + + +struct hb_atomic_short_t +{ + hb_atomic_short_t () = default; + constexpr hb_atomic_short_t (short v) : v (v) {} + + hb_atomic_short_t& operator = (short v_) { set_relaxed (v_); return *this; } + operator short () const { return get_relaxed (); } + + void set_relaxed (short v_) { hb_atomic_int_impl_set_relaxed (&v, v_); } + void set_release (short v_) { hb_atomic_int_impl_set (&v, v_); } + short get_relaxed () const { return hb_atomic_int_impl_get_relaxed (&v); } + short get_acquire () const { return hb_atomic_int_impl_get (&v); } + short inc () { return hb_atomic_int_impl_add (&v, 1); } + short dec () { return hb_atomic_int_impl_add (&v, -1); } + + short v = 0; +}; + +struct hb_atomic_int_t +{ + hb_atomic_int_t () = default; + constexpr hb_atomic_int_t (int v) : v (v) {} + + hb_atomic_int_t& operator = (int v_) { set_relaxed (v_); return *this; } + operator int () const { return get_relaxed (); } + + void set_relaxed (int v_) { hb_atomic_int_impl_set_relaxed (&v, v_); } + void set_release (int v_) { hb_atomic_int_impl_set (&v, v_); } + int get_relaxed () const { return hb_atomic_int_impl_get_relaxed (&v); } + int get_acquire () const { return hb_atomic_int_impl_get (&v); } + int inc () { return hb_atomic_int_impl_add (&v, 1); } + int dec () { return hb_atomic_int_impl_add (&v, -1); } + + int v = 0; +}; + +template +struct hb_atomic_ptr_t +{ + typedef hb_remove_pointer

T; + + hb_atomic_ptr_t () = default; + constexpr hb_atomic_ptr_t (T* v) : v (v) {} + hb_atomic_ptr_t (const hb_atomic_ptr_t &other) = delete; + + void init (T* v_ = nullptr) { set_relaxed (v_); } + void set_relaxed (T* v_) { hb_atomic_ptr_impl_set_relaxed (&v, v_); } + T *get_relaxed () const { return (T *) hb_atomic_ptr_impl_get_relaxed (&v); } + T *get_acquire () const { return (T *) hb_atomic_ptr_impl_get ((void **) &v); } + bool cmpexch (const T *old, T *new_) const { return hb_atomic_ptr_impl_cmpexch ((void **) &v, (void *) old, (void *) new_); } + + T * operator -> () const { return get_acquire (); } + template operator C * () const { return get_acquire (); } + + T *v = nullptr; +}; + + +#endif /* HB_ATOMIC_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-bimap.hh b/src/3rdparty/harfbuzz-ng/src/hb-bimap.hh new file mode 100644 index 00000000000..f541472544a --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-bimap.hh @@ -0,0 +1,205 @@ +/* + * Copyright © 2019 Adobe 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. + * + * Adobe Author(s): Michiharu Ariza + */ + +#ifndef HB_BIMAP_HH +#define HB_BIMAP_HH + +#include "hb.hh" +#include "hb-map.hh" + +/* Bi-directional map */ +struct hb_bimap_t +{ + void reset () + { + forw_map.reset (); + back_map.reset (); + } + + void alloc (unsigned pop) + { + forw_map.alloc (pop); + back_map.alloc (pop); + } + + bool in_error () const { return forw_map.in_error () || back_map.in_error (); } + + void set (hb_codepoint_t lhs, hb_codepoint_t rhs) + { + if (in_error ()) return; + if (unlikely (lhs == HB_MAP_VALUE_INVALID)) return; + if (unlikely (rhs == HB_MAP_VALUE_INVALID)) { del (lhs); return; } + + forw_map.set (lhs, rhs); + if (unlikely (in_error ())) return; + + back_map.set (rhs, lhs); + if (unlikely (in_error ())) forw_map.del (lhs); + } + + hb_codepoint_t get (hb_codepoint_t lhs) const { return forw_map.get (lhs); } + hb_codepoint_t backward (hb_codepoint_t rhs) const { return back_map.get (rhs); } + + hb_codepoint_t operator [] (hb_codepoint_t lhs) const { return get (lhs); } + bool has (hb_codepoint_t lhs) const { return forw_map.has (lhs); } + + + void del (hb_codepoint_t lhs) + { + back_map.del (get (lhs)); + forw_map.del (lhs); + } + + void clear () + { + forw_map.clear (); + back_map.clear (); + } + + bool is_empty () const { return forw_map.is_empty (); } + + unsigned int get_population () const { return forw_map.get_population (); } + + protected: + hb_map_t forw_map; + hb_map_t back_map; + + public: + auto keys () const HB_AUTO_RETURN (+ forw_map.keys()) + auto values () const HB_AUTO_RETURN (+ forw_map.values()) + auto iter () const HB_AUTO_RETURN (+ forw_map.iter()) +}; + +/* Incremental bimap: only lhs is given, rhs is incrementally assigned */ +struct hb_inc_bimap_t +{ + bool in_error () const { return forw_map.in_error () || back_map.in_error (); } + + unsigned int get_population () const { return forw_map.get_population (); } + + void reset () + { + forw_map.reset (); + back_map.reset (); + } + + void alloc (unsigned pop) + { + forw_map.alloc (pop); + back_map.alloc (pop); + } + + void clear () + { + forw_map.clear (); + back_map.resize (0); + } + + /* Add a mapping from lhs to rhs with a unique value if lhs is unknown. + * Return the rhs value as the result. + */ + hb_codepoint_t add (hb_codepoint_t lhs) + { + hb_codepoint_t rhs = forw_map[lhs]; + if (rhs == HB_MAP_VALUE_INVALID) + { + rhs = back_map.length; + forw_map.set (lhs, rhs); + back_map.push (lhs); + } + return rhs; + } + + hb_codepoint_t skip () + { + hb_codepoint_t start = back_map.length; + back_map.push (HB_MAP_VALUE_INVALID); + return start; + } + + hb_codepoint_t skip (unsigned count) + { + hb_codepoint_t start = back_map.length; + back_map.alloc (back_map.length + count); + for (unsigned i = 0; i < count; i++) + back_map.push (HB_MAP_VALUE_INVALID); + return start; + } + + hb_codepoint_t get_next_value () const + { return back_map.length; } + + void add_set (const hb_set_t *set) + { + for (auto i : *set) add (i); + } + + /* Create an identity map. */ + bool identity (unsigned int size) + { + clear (); + for (hb_codepoint_t i = 0; i < size; i++) add (i); + return !in_error (); + } + + protected: + static int cmp_id (const void* a, const void* b) + { return (int)*(const hb_codepoint_t *)a - (int)*(const hb_codepoint_t *)b; } + + public: + /* Optional: after finished adding all mappings in a random order, + * reassign rhs to lhs so that they are in the same order. */ + void sort () + { + hb_codepoint_t count = get_population (); + hb_vector_t work; + if (unlikely (!work.resize (count, false))) return; + + for (hb_codepoint_t rhs = 0; rhs < count; rhs++) + work.arrayZ[rhs] = back_map[rhs]; + + work.qsort (cmp_id); + + clear (); + for (hb_codepoint_t rhs = 0; rhs < count; rhs++) + add (work.arrayZ[rhs]); + } + + hb_codepoint_t get (hb_codepoint_t lhs) const { return forw_map.get (lhs); } + hb_codepoint_t backward (hb_codepoint_t rhs) const { return back_map[rhs]; } + + hb_codepoint_t operator [] (hb_codepoint_t lhs) const { return get (lhs); } + bool has (hb_codepoint_t lhs) const { return forw_map.has (lhs); } + + protected: + hb_map_t forw_map; + hb_vector_t back_map; + + public: + auto keys () const HB_AUTO_RETURN (+ back_map.iter()) +}; + +#endif /* HB_BIMAP_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-bit-page.hh b/src/3rdparty/harfbuzz-ng/src/hb-bit-page.hh new file mode 100644 index 00000000000..869c6789573 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-bit-page.hh @@ -0,0 +1,352 @@ +/* + * Copyright © 2012,2017 Google, Inc. + * Copyright © 2021 Behdad Esfahbod + * + * 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): Behdad Esfahbod + */ + +#ifndef HB_BIT_PAGE_HH +#define HB_BIT_PAGE_HH + +#include "hb.hh" + + +/* Compiler-assisted vectorization. */ + +/* Type behaving similar to vectorized vars defined using __attribute__((vector_size(...))), + * basically a fixed-size bitset. We can't use the compiler type because hb_vector_t cannot + * guarantee alignment requirements. */ +template +struct hb_vector_size_t +{ + elt_t& operator [] (unsigned int i) { return v[i]; } + const elt_t& operator [] (unsigned int i) const { return v[i]; } + + void init0 () + { + for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++) + v[i] = 0; + } + void init1 () + { + for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++) + v[i] = (elt_t) -1; + } + + template + hb_vector_size_t process (const Op& op) const + { + hb_vector_size_t r; + for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++) + r.v[i] = op (v[i]); + return r; + } + template + hb_vector_size_t process (const Op& op, const hb_vector_size_t &o) const + { + hb_vector_size_t r; + for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++) + r.v[i] = op (v[i], o.v[i]); + return r; + } + hb_vector_size_t operator | (const hb_vector_size_t &o) const + { return process (hb_bitwise_or, o); } + hb_vector_size_t operator & (const hb_vector_size_t &o) const + { return process (hb_bitwise_and, o); } + hb_vector_size_t operator ^ (const hb_vector_size_t &o) const + { return process (hb_bitwise_xor, o); } + hb_vector_size_t operator ~ () const + { return process (hb_bitwise_neg); } + + hb_array_t iter () const + { return hb_array (v); } + + private: + static_assert (0 == byte_size % sizeof (elt_t), ""); + elt_t v[byte_size / sizeof (elt_t)]; +}; + + +struct hb_bit_page_t +{ + void init0 () { v.init0 (); population = 0; } + void init1 () { v.init1 (); population = PAGE_BITS; } + + void dirty () { population = UINT_MAX; } + + static inline constexpr unsigned len () + { return ARRAY_LENGTH_CONST (v); } + + operator bool () const { return !is_empty (); } + bool is_empty () const + { + if (has_population ()) return !population; + return + + hb_iter (v) + | hb_none + ; + } + uint32_t hash () const + { + return hb_bytes_t ((const char *) &v, sizeof (v)).hash (); + } + + void add (hb_codepoint_t g) { elt (g) |= mask (g); dirty (); } + void del (hb_codepoint_t g) { elt (g) &= ~mask (g); dirty (); } + void set (hb_codepoint_t g, bool value) { if (value) add (g); else del (g); } + bool get (hb_codepoint_t g) const { return elt (g) & mask (g); } + + void add_range (hb_codepoint_t a, hb_codepoint_t b) + { + elt_t *la = &elt (a); + elt_t *lb = &elt (b); + if (la == lb) + *la |= (mask (b) << 1) - mask(a); + else + { + *la |= ~(mask (a) - 1llu); + la++; + + hb_memset (la, 0xff, (char *) lb - (char *) la); + + *lb |= ((mask (b) << 1) - 1llu); + } + dirty (); + } + void del_range (hb_codepoint_t a, hb_codepoint_t b) + { + elt_t *la = &elt (a); + elt_t *lb = &elt (b); + if (la == lb) + *la &= ~((mask (b) << 1llu) - mask(a)); + else + { + *la &= mask (a) - 1; + la++; + + hb_memset (la, 0, (char *) lb - (char *) la); + + *lb &= ~((mask (b) << 1) - 1llu); + } + dirty (); + } + void set_range (hb_codepoint_t a, hb_codepoint_t b, bool v) + { if (v) add_range (a, b); else del_range (a, b); } + + + // Writes out page values to the array p. Returns the number of values + // written. At most size codepoints will be written. + unsigned int write (uint32_t base, + unsigned int start_value, + hb_codepoint_t *p, + unsigned int size) const + { + unsigned int start_v = start_value / ELT_BITS; + unsigned int start_bit = start_value & ELT_MASK; + unsigned int count = 0; + for (unsigned i = start_v; i < len () && count < size; i++) + { + elt_t bits = v[i]; + uint32_t v_base = base | (i * ELT_BITS); + for (unsigned int j = start_bit; j < ELT_BITS && count < size; j++) + { + if ((elt_t(1) << j) & bits) { + *p++ = v_base | j; + count++; + } + } + start_bit = 0; + } + return count; + } + + // Writes out the values NOT in this page to the array p. Returns the + // number of values written. At most size codepoints will be written. + // Returns the number of codepoints written. next_value holds the next value + // that should be written (if not present in this page). This is used to fill + // any missing value gaps between this page and the previous page, if any. + // next_value is updated to one more than the last value present in this page. + unsigned int write_inverted (uint32_t base, + unsigned int start_value, + hb_codepoint_t *p, + unsigned int size, + hb_codepoint_t *next_value) const + { + unsigned int start_v = start_value / ELT_BITS; + unsigned int start_bit = start_value & ELT_MASK; + unsigned int count = 0; + for (unsigned i = start_v; i < len () && count < size; i++) + { + elt_t bits = v[i]; + uint32_t v_offset = i * ELT_BITS; + for (unsigned int j = start_bit; j < ELT_BITS && count < size; j++) + { + if ((elt_t(1) << j) & bits) + { + hb_codepoint_t value = base | v_offset | j; + // Emit all the missing values from next_value up to value - 1. + for (hb_codepoint_t k = *next_value; k < value && count < size; k++) + { + *p++ = k; + count++; + } + // Skip over this value; + *next_value = value + 1; + } + } + start_bit = 0; + } + return count; + } + + bool operator == (const hb_bit_page_t &other) const { return is_equal (other); } + bool is_equal (const hb_bit_page_t &other) const + { + for (unsigned i = 0; i < len (); i++) + if (v[i] != other.v[i]) + return false; + return true; + } + bool operator <= (const hb_bit_page_t &larger_page) const { return is_subset (larger_page); } + bool is_subset (const hb_bit_page_t &larger_page) const + { + if (has_population () && larger_page.has_population () && + population > larger_page.population) + return false; + + for (unsigned i = 0; i < len (); i++) + if (~larger_page.v[i] & v[i]) + return false; + return true; + } + + bool has_population () const { return population != UINT_MAX; } + unsigned int get_population () const + { + if (has_population ()) return population; + population = + + hb_iter (v) + | hb_reduce ([] (unsigned pop, const elt_t &_) { return pop + hb_popcount (_); }, 0u) + ; + return population; + } + + bool next (hb_codepoint_t *codepoint) const + { + unsigned int m = (*codepoint + 1) & MASK; + if (!m) + { + *codepoint = INVALID; + return false; + } + unsigned int i = m / ELT_BITS; + unsigned int j = m & ELT_MASK; + + const elt_t vv = v[i] & ~((elt_t (1) << j) - 1); + for (const elt_t *p = &vv; i < len (); p = &v[++i]) + if (*p) + { + *codepoint = i * ELT_BITS + elt_get_min (*p); + return true; + } + + *codepoint = INVALID; + return false; + } + bool previous (hb_codepoint_t *codepoint) const + { + unsigned int m = (*codepoint - 1) & MASK; + if (m == MASK) + { + *codepoint = INVALID; + return false; + } + unsigned int i = m / ELT_BITS; + unsigned int j = m & ELT_MASK; + + /* Fancy mask to avoid shifting by elt_t bitsize, which is undefined. */ + const elt_t mask = j < 8 * sizeof (elt_t) - 1 ? + ((elt_t (1) << (j + 1)) - 1) : + (elt_t) -1; + const elt_t vv = v[i] & mask; + const elt_t *p = &vv; + while (true) + { + if (*p) + { + *codepoint = i * ELT_BITS + elt_get_max (*p); + return true; + } + if ((int) i <= 0) break; + p = &v[--i]; + } + + *codepoint = INVALID; + return false; + } + hb_codepoint_t get_min () const + { + for (unsigned int i = 0; i < len (); i++) + if (v[i]) + return i * ELT_BITS + elt_get_min (v[i]); + return INVALID; + } + hb_codepoint_t get_max () const + { + for (int i = len () - 1; i >= 0; i--) + if (v[i]) + return i * ELT_BITS + elt_get_max (v[i]); + return 0; + } + + static constexpr hb_codepoint_t INVALID = HB_SET_VALUE_INVALID; + + typedef unsigned long long elt_t; + static constexpr unsigned PAGE_BITS_LOG_2 = 9; // 512 bits + static constexpr unsigned PAGE_BITS = 1 << PAGE_BITS_LOG_2; + static_assert (1 << PAGE_BITS_LOG_2 == PAGE_BITS, ""); + static_assert ((PAGE_BITS & ((PAGE_BITS) - 1)) == 0, ""); + static constexpr unsigned PAGE_BITMASK = PAGE_BITS - 1; + + static unsigned int elt_get_min (const elt_t &elt) { return hb_ctz (elt); } + static unsigned int elt_get_max (const elt_t &elt) { return hb_bit_storage (elt) - 1; } + + typedef hb_vector_size_t vector_t; + + static constexpr unsigned ELT_BITS = sizeof (elt_t) * 8; + static constexpr unsigned ELT_MASK = ELT_BITS - 1; + + static constexpr unsigned BITS = sizeof (vector_t) * 8; + static constexpr unsigned MASK = BITS - 1; + static_assert ((unsigned) PAGE_BITS == (unsigned) BITS, ""); + + elt_t &elt (hb_codepoint_t g) { return v[(g & MASK) / ELT_BITS]; } + const elt_t& elt (hb_codepoint_t g) const { return v[(g & MASK) / ELT_BITS]; } + static constexpr elt_t mask (hb_codepoint_t g) { return elt_t (1) << (g & ELT_MASK); } + + mutable unsigned population; + vector_t v; +}; + + +#endif /* HB_BIT_PAGE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-bit-set-invertible.hh b/src/3rdparty/harfbuzz-ng/src/hb-bit-set-invertible.hh new file mode 100644 index 00000000000..e765a479ae6 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-bit-set-invertible.hh @@ -0,0 +1,379 @@ +/* + * Copyright © 2012,2017 Google, Inc. + * Copyright © 2021 Behdad Esfahbod + * + * 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): Behdad Esfahbod + */ + +#ifndef HB_BIT_SET_INVERTIBLE_HH +#define HB_BIT_SET_INVERTIBLE_HH + +#include "hb.hh" +#include "hb-bit-set.hh" + + +struct hb_bit_set_invertible_t +{ + hb_bit_set_t s; + bool inverted = false; + + hb_bit_set_invertible_t () = default; + hb_bit_set_invertible_t (const hb_bit_set_invertible_t& o) = default; + hb_bit_set_invertible_t (hb_bit_set_invertible_t&& other) : hb_bit_set_invertible_t () { hb_swap (*this, other); } + hb_bit_set_invertible_t& operator= (const hb_bit_set_invertible_t& o) = default; + hb_bit_set_invertible_t& operator= (hb_bit_set_invertible_t&& other) { hb_swap (*this, other); return *this; } + friend void swap (hb_bit_set_invertible_t &a, hb_bit_set_invertible_t &b) + { + if (likely (!a.s.successful || !b.s.successful)) + return; + hb_swap (a.inverted, b.inverted); + hb_swap (a.s, b.s); + } + + void init () { s.init (); inverted = false; } + void fini () { s.fini (); } + void err () { s.err (); } + bool in_error () const { return s.in_error (); } + explicit operator bool () const { return !is_empty (); } + + void alloc (unsigned sz) { s.alloc (sz); } + void reset () + { + s.reset (); + inverted = false; + } + void clear () + { + s.clear (); + if (likely (s.successful)) + inverted = false; + } + void invert () + { + if (likely (s.successful)) + inverted = !inverted; + } + + bool is_inverted () const + { + return inverted; + } + + bool is_empty () const + { + hb_codepoint_t v = INVALID; + next (&v); + return v == INVALID; + } + uint32_t hash () const { return s.hash () ^ (uint32_t) inverted; } + + hb_codepoint_t get_min () const + { + hb_codepoint_t v = INVALID; + next (&v); + return v; + } + hb_codepoint_t get_max () const + { + hb_codepoint_t v = INVALID; + previous (&v); + return v; + } + unsigned int get_population () const + { return inverted ? INVALID - s.get_population () : s.get_population (); } + + + void add (hb_codepoint_t g) { unlikely (inverted) ? s.del (g) : s.add (g); } + bool add_range (hb_codepoint_t a, hb_codepoint_t b) + { return unlikely (inverted) ? ((void) s.del_range (a, b), true) : s.add_range (a, b); } + + template + void add_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { inverted ? s.del_array (array, count, stride) : s.add_array (array, count, stride); } + template + void add_array (const hb_array_t& arr) { add_array (&arr, arr.len ()); } + + /* Might return false if array looks unsorted. + * Used for faster rejection of corrupt data. */ + template + bool add_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { return inverted ? s.del_sorted_array (array, count, stride) : s.add_sorted_array (array, count, stride); } + template + bool add_sorted_array (const hb_sorted_array_t& arr) { return add_sorted_array (&arr, arr.len ()); } + + void del (hb_codepoint_t g) { unlikely (inverted) ? s.add (g) : s.del (g); } + void del_range (hb_codepoint_t a, hb_codepoint_t b) + { unlikely (inverted) ? (void) s.add_range (a, b) : s.del_range (a, b); } + + bool get (hb_codepoint_t g) const { return s.get (g) ^ inverted; } + + /* Has interface. */ + bool operator [] (hb_codepoint_t k) const { return get (k); } + bool has (hb_codepoint_t k) const { return (*this)[k]; } + /* Predicate. */ + bool operator () (hb_codepoint_t k) const { return has (k); } + + /* Sink interface. */ + hb_bit_set_invertible_t& operator << (hb_codepoint_t v) + { add (v); return *this; } + hb_bit_set_invertible_t& operator << (const hb_codepoint_pair_t& range) + { add_range (range.first, range.second); return *this; } + + bool intersects (hb_codepoint_t first, hb_codepoint_t last) const + { + hb_codepoint_t c = first - 1; + return next (&c) && c <= last; + } + + void set (const hb_bit_set_invertible_t &other) + { + s.set (other.s); + if (likely (s.successful)) + inverted = other.inverted; + } + + bool is_equal (const hb_bit_set_invertible_t &other) const + { + if (likely (inverted == other.inverted)) + return s.is_equal (other.s); + else + { + /* TODO Add iter_ranges() and use here. */ + auto it1 = iter (); + auto it2 = other.iter (); + return hb_all (+ hb_zip (it1, it2) + | hb_map ([](hb_codepoint_pair_t _) { return _.first == _.second; })); + } + } + + bool is_subset (const hb_bit_set_invertible_t &larger_set) const + { + if (unlikely (inverted != larger_set.inverted)) + return hb_all (hb_iter (s) | hb_map (larger_set.s)); + else + return unlikely (inverted) ? larger_set.s.is_subset (s) : s.is_subset (larger_set.s); + } + + protected: + template + void process (const Op& op, const hb_bit_set_invertible_t &other) + { s.process (op, other.s); } + public: + void union_ (const hb_bit_set_invertible_t &other) + { + if (likely (inverted == other.inverted)) + { + if (unlikely (inverted)) + process (hb_bitwise_and, other); + else + process (hb_bitwise_or, other); /* Main branch. */ + } + else + { + if (unlikely (inverted)) + process (hb_bitwise_gt, other); + else + process (hb_bitwise_lt, other); + } + if (likely (s.successful)) + inverted = inverted || other.inverted; + } + void intersect (const hb_bit_set_invertible_t &other) + { + if (likely (inverted == other.inverted)) + { + if (unlikely (inverted)) + process (hb_bitwise_or, other); + else + process (hb_bitwise_and, other); /* Main branch. */ + } + else + { + if (unlikely (inverted)) + process (hb_bitwise_lt, other); + else + process (hb_bitwise_gt, other); + } + if (likely (s.successful)) + inverted = inverted && other.inverted; + } + void subtract (const hb_bit_set_invertible_t &other) + { + if (likely (inverted == other.inverted)) + { + if (unlikely (inverted)) + process (hb_bitwise_lt, other); + else + process (hb_bitwise_gt, other); /* Main branch. */ + } + else + { + if (unlikely (inverted)) + process (hb_bitwise_or, other); + else + process (hb_bitwise_and, other); + } + if (likely (s.successful)) + inverted = inverted && !other.inverted; + } + void symmetric_difference (const hb_bit_set_invertible_t &other) + { + process (hb_bitwise_xor, other); + if (likely (s.successful)) + inverted = inverted ^ other.inverted; + } + + bool next (hb_codepoint_t *codepoint) const + { + if (likely (!inverted)) + return s.next (codepoint); + + auto old = *codepoint; + if (unlikely (old + 1 == INVALID)) + { + *codepoint = INVALID; + return false; + } + + auto v = old; + s.next (&v); + if (old + 1 < v) + { + *codepoint = old + 1; + return true; + } + + v = old; + s.next_range (&old, &v); + + *codepoint = v + 1; + return *codepoint != INVALID; + } + bool previous (hb_codepoint_t *codepoint) const + { + if (likely (!inverted)) + return s.previous (codepoint); + + auto old = *codepoint; + if (unlikely (old - 1 == INVALID)) + { + *codepoint = INVALID; + return false; + } + + auto v = old; + s.previous (&v); + + if (old - 1 > v || v == INVALID) + { + *codepoint = old - 1; + return true; + } + + v = old; + s.previous_range (&v, &old); + + *codepoint = v - 1; + return *codepoint != INVALID; + } + bool next_range (hb_codepoint_t *first, hb_codepoint_t *last) const + { + if (likely (!inverted)) + return s.next_range (first, last); + + if (!next (last)) + { + *last = *first = INVALID; + return false; + } + + *first = *last; + s.next (last); + --*last; + return true; + } + bool previous_range (hb_codepoint_t *first, hb_codepoint_t *last) const + { + if (likely (!inverted)) + return s.previous_range (first, last); + + if (!previous (first)) + { + *last = *first = INVALID; + return false; + } + + *last = *first; + s.previous (first); + ++*first; + return true; + } + + unsigned int next_many (hb_codepoint_t codepoint, + hb_codepoint_t *out, + unsigned int size) const + { + return inverted ? s.next_many_inverted (codepoint, out, size) + : s.next_many (codepoint, out, size); + } + + static constexpr hb_codepoint_t INVALID = hb_bit_set_t::INVALID; + + /* + * Iterator implementation. + */ + struct iter_t : hb_iter_with_fallback_t + { + static constexpr bool is_sorted_iterator = true; + static constexpr bool has_fast_len = true; + iter_t (const hb_bit_set_invertible_t &s_ = Null (hb_bit_set_invertible_t), + bool init = true) : s (&s_), v (INVALID), l(0) + { + if (init) + { + l = s->get_population () + 1; + __next__ (); + } + } + + typedef hb_codepoint_t __item_t__; + hb_codepoint_t __item__ () const { return v; } + bool __more__ () const { return v != INVALID; } + void __next__ () { s->next (&v); if (l) l--; } + void __prev__ () { s->previous (&v); } + unsigned __len__ () const { return l; } + iter_t end () const { return iter_t (*s, false); } + bool operator != (const iter_t& o) const + { return v != o.v || s != o.s; } + + protected: + const hb_bit_set_invertible_t *s; + hb_codepoint_t v; + unsigned l; + }; + iter_t iter () const { return iter_t (*this); } + operator iter_t () const { return iter (); } +}; + + +#endif /* HB_BIT_SET_INVERTIBLE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-bit-set.hh b/src/3rdparty/harfbuzz-ng/src/hb-bit-set.hh new file mode 100644 index 00000000000..1dbcce5cbd5 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-bit-set.hh @@ -0,0 +1,982 @@ +/* + * Copyright © 2012,2017 Google, Inc. + * Copyright © 2021 Behdad Esfahbod + * + * 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): Behdad Esfahbod + */ + +#ifndef HB_BIT_SET_HH +#define HB_BIT_SET_HH + +#include "hb.hh" +#include "hb-bit-page.hh" + + +struct hb_bit_set_t +{ + hb_bit_set_t () = default; + ~hb_bit_set_t () = default; + + hb_bit_set_t (const hb_bit_set_t& other) : hb_bit_set_t () { set (other, true); } + hb_bit_set_t ( hb_bit_set_t&& other) : hb_bit_set_t () { hb_swap (*this, other); } + hb_bit_set_t& operator= (const hb_bit_set_t& other) { set (other); return *this; } + hb_bit_set_t& operator= (hb_bit_set_t&& other) { hb_swap (*this, other); return *this; } + friend void swap (hb_bit_set_t &a, hb_bit_set_t &b) + { + if (likely (!a.successful || !b.successful)) + return; + hb_swap (a.population, b.population); + hb_swap (a.last_page_lookup, b.last_page_lookup); + hb_swap (a.page_map, b.page_map); + hb_swap (a.pages, b.pages); + } + + void init () + { + successful = true; + population = 0; + last_page_lookup = 0; + page_map.init (); + pages.init (); + } + void fini () + { + page_map.fini (); + pages.fini (); + } + + using page_t = hb_bit_page_t; + struct page_map_t + { + int cmp (const page_map_t &o) const { return cmp (o.major); } + int cmp (uint32_t o_major) const { return (int) o_major - (int) major; } + + uint32_t major; + uint32_t index; + }; + + bool successful = true; /* Allocations successful */ + mutable unsigned int population = 0; + mutable hb_atomic_int_t last_page_lookup = 0; + hb_sorted_vector_t page_map; + hb_vector_t pages; + + void err () { if (successful) successful = false; } /* TODO Remove */ + bool in_error () const { return !successful; } + + bool resize (unsigned int count, bool clear = true, bool exact_size = false) + { + if (unlikely (!successful)) return false; + + if (pages.length == 0 && count == 1) + exact_size = true; // Most sets are small and local + + if (unlikely (!pages.resize (count, clear, exact_size) || !page_map.resize (count, clear, exact_size))) + { + pages.resize (page_map.length, clear, exact_size); + successful = false; + return false; + } + return true; + } + + void alloc (unsigned sz) + { + sz >>= (page_t::PAGE_BITS_LOG_2 - 1); + pages.alloc (sz); + page_map.alloc (sz); + } + + void reset () + { + successful = true; + clear (); + } + + void clear () + { + resize (0); + if (likely (successful)) + population = 0; + } + bool is_empty () const + { + unsigned int count = pages.length; + for (unsigned int i = 0; i < count; i++) + if (!pages[i].is_empty ()) + return false; + return true; + } + explicit operator bool () const { return !is_empty (); } + + uint32_t hash () const + { + uint32_t h = 0; + for (auto &map : page_map) + { + auto &page = pages.arrayZ[map.index]; + if (unlikely (page.is_empty ())) continue; + h = h * 31 + hb_hash (map.major) + hb_hash (page); + } + return h; + } + + private: + void dirty () { population = UINT_MAX; } + public: + + void add (hb_codepoint_t g) + { + if (unlikely (!successful)) return; + if (unlikely (g == INVALID)) return; + dirty (); + page_t *page = page_for (g, true); if (unlikely (!page)) return; + page->add (g); + } + bool add_range (hb_codepoint_t a, hb_codepoint_t b) + { + if (unlikely (!successful)) return true; /* https://github.com/harfbuzz/harfbuzz/issues/657 */ + if (unlikely (a > b || a == INVALID || b == INVALID)) return false; + dirty (); + unsigned int ma = get_major (a); + unsigned int mb = get_major (b); + if (ma == mb) + { + page_t *page = page_for (a, true); if (unlikely (!page)) return false; + page->add_range (a, b); + } + else + { + page_t *page = page_for (a, true); if (unlikely (!page)) return false; + page->add_range (a, major_start (ma + 1) - 1); + + for (unsigned int m = ma + 1; m < mb; m++) + { + page = page_for (major_start (m), true); if (unlikely (!page)) return false; + page->init1 (); + } + + page = page_for (b, true); if (unlikely (!page)) return false; + page->add_range (major_start (mb), b); + } + return true; + } + + /* Duplicated here from hb-machinery.hh to avoid including it. */ + template + static inline const Type& StructAtOffsetUnaligned(const void *P, unsigned int offset) + { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-align" + return * reinterpret_cast ((const char *) P + offset); +#pragma GCC diagnostic pop + } + + template + void set_array (bool v, const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { + if (unlikely (!successful)) return; + if (!count) return; + dirty (); + hb_codepoint_t g = *array; + while (count) + { + unsigned int m = get_major (g); + page_t *page = page_for (g, v); if (unlikely (v && !page)) return; + unsigned int start = major_start (m); + unsigned int end = major_start (m + 1); + do + { + if (g != INVALID && (v || page)) /* The v check is to optimize out the page check if v is true. */ + page->set (g, v); + + array = &StructAtOffsetUnaligned (array, stride); + count--; + } + while (count && (g = *array, start <= g && g < end)); + } + } + + template + void add_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { set_array (true, array, count, stride); } + template + void add_array (const hb_array_t& arr) { add_array (&arr, arr.len ()); } + + template + void del_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { set_array (false, array, count, stride); } + template + void del_array (const hb_array_t& arr) { del_array (&arr, arr.len ()); } + + /* Might return false if array looks unsorted. + * Used for faster rejection of corrupt data. */ + template + bool set_sorted_array (bool v, const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { + if (unlikely (!successful)) return true; /* https://github.com/harfbuzz/harfbuzz/issues/657 */ + if (unlikely (!count)) return true; + dirty (); + hb_codepoint_t g = *array; + hb_codepoint_t last_g = g; + while (count) + { + unsigned int m = get_major (g); + page_t *page = page_for (g, v); if (unlikely (v && !page)) return false; + unsigned int end = major_start (m + 1); + do + { + /* If we try harder we can change the following comparison to <=; + * Not sure if it's worth it. */ + if (g < last_g) return false; + last_g = g; + + if (g != INVALID && (v || page)) /* The v check is to optimize out the page check if v is true. */ + page->add (g); + + array = &StructAtOffsetUnaligned (array, stride); + count--; + } + while (count && (g = *array, g < end)); + } + return true; + } + + template + bool add_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { return set_sorted_array (true, array, count, stride); } + template + bool add_sorted_array (const hb_sorted_array_t& arr) { return add_sorted_array (&arr, arr.len ()); } + + template + bool del_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { return set_sorted_array (false, array, count, stride); } + template + bool del_sorted_array (const hb_sorted_array_t& arr) { return del_sorted_array (&arr, arr.len ()); } + + void del (hb_codepoint_t g) + { + if (unlikely (!successful)) return; + page_t *page = page_for (g); + if (!page) + return; + dirty (); + page->del (g); + } + + private: + void del_pages (int ds, int de) + { + if (ds <= de) + { + // Pre-allocate the workspace that compact() will need so we can bail on allocation failure + // before attempting to rewrite the page map. + hb_vector_t compact_workspace; + if (unlikely (!allocate_compact_workspace (compact_workspace))) return; + + unsigned int write_index = 0; + for (unsigned int i = 0; i < page_map.length; i++) + { + int m = (int) page_map[i].major; + if (m < ds || de < m) + page_map[write_index++] = page_map[i]; + } + compact (compact_workspace, write_index); + resize (write_index); + } + } + + + public: + void del_range (hb_codepoint_t a, hb_codepoint_t b) + { + if (unlikely (!successful)) return; + if (unlikely (a > b || a == INVALID)) return; + dirty (); + unsigned int ma = get_major (a); + unsigned int mb = get_major (b); + /* Delete pages from ds through de if ds <= de. */ + int ds = (a == major_start (ma))? (int) ma: (int) (ma + 1); + int de = (b + 1 == major_start (mb + 1))? (int) mb: ((int) mb - 1); + if (ds > de || (int) ma < ds) + { + page_t *page = page_for (a); + if (page) + { + if (ma == mb) + page->del_range (a, b); + else + page->del_range (a, major_start (ma + 1) - 1); + } + } + if (de < (int) mb && ma != mb) + { + page_t *page = page_for (b); + if (page) + page->del_range (major_start (mb), b); + } + del_pages (ds, de); + } + + bool get (hb_codepoint_t g) const + { + const page_t *page = page_for (g); + if (!page) + return false; + return page->get (g); + } + + /* Has interface. */ + bool operator [] (hb_codepoint_t k) const { return get (k); } + bool has (hb_codepoint_t k) const { return (*this)[k]; } + /* Predicate. */ + bool operator () (hb_codepoint_t k) const { return has (k); } + + /* Sink interface. */ + hb_bit_set_t& operator << (hb_codepoint_t v) + { add (v); return *this; } + hb_bit_set_t& operator << (const hb_codepoint_pair_t& range) + { add_range (range.first, range.second); return *this; } + + bool intersects (hb_codepoint_t first, hb_codepoint_t last) const + { + hb_codepoint_t c = first - 1; + return next (&c) && c <= last; + } + void set (const hb_bit_set_t &other, bool exact_size = false) + { + if (unlikely (!successful)) return; + unsigned int count = other.pages.length; + if (unlikely (!resize (count, false, exact_size))) + return; + population = other.population; + + page_map = other.page_map; + pages = other.pages; + } + + bool is_equal (const hb_bit_set_t &other) const + { + if (has_population () && other.has_population () && + population != other.population) + return false; + + unsigned int na = pages.length; + unsigned int nb = other.pages.length; + + unsigned int a = 0, b = 0; + for (; a < na && b < nb; ) + { + if (page_at (a).is_empty ()) { a++; continue; } + if (other.page_at (b).is_empty ()) { b++; continue; } + if (page_map[a].major != other.page_map[b].major || + !page_at (a).is_equal (other.page_at (b))) + return false; + a++; + b++; + } + for (; a < na; a++) + if (!page_at (a).is_empty ()) { return false; } + for (; b < nb; b++) + if (!other.page_at (b).is_empty ()) { return false; } + + return true; + } + + bool is_subset (const hb_bit_set_t &larger_set) const + { + if (has_population () && larger_set.has_population () && + population > larger_set.population) + return false; + + uint32_t spi = 0; + for (uint32_t lpi = 0; spi < page_map.length && lpi < larger_set.page_map.length; lpi++) + { + uint32_t spm = page_map[spi].major; + uint32_t lpm = larger_set.page_map[lpi].major; + auto sp = page_at (spi); + + if (spm < lpm && !sp.is_empty ()) + return false; + + if (lpm < spm) + continue; + + auto lp = larger_set.page_at (lpi); + if (!sp.is_subset (lp)) + return false; + + spi++; + } + + while (spi < page_map.length) + if (!page_at (spi++).is_empty ()) + return false; + + return true; + } + + private: + bool allocate_compact_workspace (hb_vector_t& workspace) + { + if (unlikely (!workspace.resize_exact (pages.length))) + { + successful = false; + return false; + } + + return true; + } + + /* + * workspace should be a pre-sized vector allocated to hold at exactly pages.length + * elements. + */ + void compact (hb_vector_t& workspace, + unsigned int length) + { + assert(workspace.length == pages.length); + hb_vector_t& old_index_to_page_map_index = workspace; + + hb_fill (old_index_to_page_map_index.writer(), 0xFFFFFFFF); + for (unsigned i = 0; i < length; i++) + old_index_to_page_map_index[page_map[i].index] = i; + + compact_pages (old_index_to_page_map_index); + } + void compact_pages (const hb_vector_t& old_index_to_page_map_index) + { + unsigned int write_index = 0; + for (unsigned int i = 0; i < pages.length; i++) + { + if (old_index_to_page_map_index[i] == 0xFFFFFFFF) continue; + + if (write_index < i) + pages[write_index] = pages[i]; + + page_map[old_index_to_page_map_index[i]].index = write_index; + write_index++; + } + } + public: + + void process_ (hb_bit_page_t::vector_t (*op) (const hb_bit_page_t::vector_t &, const hb_bit_page_t::vector_t &), + bool passthru_left, bool passthru_right, + const hb_bit_set_t &other) + { + if (unlikely (!successful)) return; + + dirty (); + + unsigned int na = pages.length; + unsigned int nb = other.pages.length; + unsigned int next_page = na; + + unsigned int count = 0, newCount = 0; + unsigned int a = 0, b = 0; + unsigned int write_index = 0; + + // Pre-allocate the workspace that compact() will need so we can bail on allocation failure + // before attempting to rewrite the page map. + hb_vector_t compact_workspace; + if (!passthru_left && unlikely (!allocate_compact_workspace (compact_workspace))) return; + + for (; a < na && b < nb; ) + { + if (page_map[a].major == other.page_map[b].major) + { + if (!passthru_left) + { + // Move page_map entries that we're keeping from the left side set + // to the front of the page_map vector. This isn't necessary if + // passthru_left is set since no left side pages will be removed + // in that case. + if (write_index < a) + page_map[write_index] = page_map[a]; + write_index++; + } + + count++; + a++; + b++; + } + else if (page_map[a].major < other.page_map[b].major) + { + if (passthru_left) + count++; + a++; + } + else + { + if (passthru_right) + count++; + b++; + } + } + if (passthru_left) + count += na - a; + if (passthru_right) + count += nb - b; + + if (!passthru_left) + { + na = write_index; + next_page = write_index; + compact (compact_workspace, write_index); + } + + if (unlikely (!resize (count))) + return; + + newCount = count; + + /* Process in-place backward. */ + a = na; + b = nb; + for (; a && b; ) + { + if (page_map.arrayZ[a - 1].major == other.page_map.arrayZ[b - 1].major) + { + a--; + b--; + count--; + page_map.arrayZ[count] = page_map.arrayZ[a]; + page_at (count).v = op (page_at (a).v, other.page_at (b).v); + page_at (count).dirty (); + } + else if (page_map.arrayZ[a - 1].major > other.page_map.arrayZ[b - 1].major) + { + a--; + if (passthru_left) + { + count--; + page_map.arrayZ[count] = page_map.arrayZ[a]; + } + } + else + { + b--; + if (passthru_right) + { + count--; + page_map.arrayZ[count].major = other.page_map.arrayZ[b].major; + page_map.arrayZ[count].index = next_page++; + page_at (count) = other.page_at (b); + } + } + } + if (passthru_left) + while (a) + { + a--; + count--; + page_map.arrayZ[count] = page_map.arrayZ[a]; + } + if (passthru_right) + while (b) + { + b--; + count--; + page_map.arrayZ[count].major = other.page_map.arrayZ[b].major; + page_map.arrayZ[count].index = next_page++; + page_at (count) = other.page_at (b); + } + assert (!count); + resize (newCount); + } + template + static hb_bit_page_t::vector_t + op_ (const hb_bit_page_t::vector_t &a, const hb_bit_page_t::vector_t &b) + { return Op{} (a, b); } + template + void process (const Op& op, const hb_bit_set_t &other) + { + process_ (op_, op (1, 0), op (0, 1), other); + } + + void union_ (const hb_bit_set_t &other) { process (hb_bitwise_or, other); } + void intersect (const hb_bit_set_t &other) { process (hb_bitwise_and, other); } + void subtract (const hb_bit_set_t &other) { process (hb_bitwise_gt, other); } + void symmetric_difference (const hb_bit_set_t &other) { process (hb_bitwise_xor, other); } + + bool next (hb_codepoint_t *codepoint) const + { + if (unlikely (*codepoint == INVALID)) { + *codepoint = get_min (); + return *codepoint != INVALID; + } + + const auto* page_map_array = page_map.arrayZ; + unsigned int major = get_major (*codepoint); + unsigned int i = last_page_lookup; + + if (unlikely (i >= page_map.length || page_map_array[i].major != major)) + { + page_map.bfind (major, &i, HB_NOT_FOUND_STORE_CLOSEST); + if (i >= page_map.length) { + *codepoint = INVALID; + return false; + } + last_page_lookup = i; + } + + const auto* pages_array = pages.arrayZ; + const page_map_t ¤t = page_map_array[i]; + if (likely (current.major == major)) + { + if (pages_array[current.index].next (codepoint)) + { + *codepoint += current.major * page_t::PAGE_BITS; + return true; + } + i++; + } + + for (; i < page_map.length; i++) + { + const page_map_t ¤t = page_map_array[i]; + hb_codepoint_t m = pages_array[current.index].get_min (); + if (m != INVALID) + { + *codepoint = current.major * page_t::PAGE_BITS + m; + last_page_lookup = i; + return true; + } + } + *codepoint = INVALID; + return false; + } + bool previous (hb_codepoint_t *codepoint) const + { + if (unlikely (*codepoint == INVALID)) { + *codepoint = get_max (); + return *codepoint != INVALID; + } + + page_map_t map = {get_major (*codepoint), 0}; + unsigned int i; + page_map.bfind (map, &i, HB_NOT_FOUND_STORE_CLOSEST); + if (i < page_map.length && page_map.arrayZ[i].major == map.major) + { + if (pages[page_map.arrayZ[i].index].previous (codepoint)) + { + *codepoint += page_map.arrayZ[i].major * page_t::PAGE_BITS; + return true; + } + } + i--; + for (; (int) i >= 0; i--) + { + hb_codepoint_t m = pages.arrayZ[page_map.arrayZ[i].index].get_max (); + if (m != INVALID) + { + *codepoint = page_map.arrayZ[i].major * page_t::PAGE_BITS + m; + return true; + } + } + *codepoint = INVALID; + return false; + } + bool next_range (hb_codepoint_t *first, hb_codepoint_t *last) const + { + hb_codepoint_t i; + + i = *last; + if (!next (&i)) + { + *last = *first = INVALID; + return false; + } + + /* TODO Speed up. */ + *last = *first = i; + while (next (&i) && i == *last + 1) + (*last)++; + + return true; + } + bool previous_range (hb_codepoint_t *first, hb_codepoint_t *last) const + { + hb_codepoint_t i; + + i = *first; + if (!previous (&i)) + { + *last = *first = INVALID; + return false; + } + + /* TODO Speed up. */ + *last = *first = i; + while (previous (&i) && i == *first - 1) + (*first)--; + + return true; + } + + unsigned int next_many (hb_codepoint_t codepoint, + hb_codepoint_t *out, + unsigned int size) const + { + // By default, start at the first bit of the first page of values. + unsigned int start_page = 0; + unsigned int start_page_value = 0; + if (unlikely (codepoint != INVALID)) + { + const auto* page_map_array = page_map.arrayZ; + unsigned int major = get_major (codepoint); + unsigned int i = last_page_lookup; + if (unlikely (i >= page_map.length || page_map_array[i].major != major)) + { + page_map.bfind (major, &i, HB_NOT_FOUND_STORE_CLOSEST); + if (i >= page_map.length) + return 0; // codepoint is greater than our max element. + } + start_page = i; + start_page_value = page_remainder (codepoint + 1); + if (unlikely (start_page_value == 0)) + { + // The export-after value was last in the page. Start on next page. + start_page++; + start_page_value = 0; + } + } + + unsigned int initial_size = size; + for (unsigned int i = start_page; i < page_map.length && size; i++) + { + uint32_t base = major_start (page_map[i].major); + unsigned int n = pages[page_map[i].index].write (base, start_page_value, out, size); + out += n; + size -= n; + start_page_value = 0; + } + return initial_size - size; + } + + unsigned int next_many_inverted (hb_codepoint_t codepoint, + hb_codepoint_t *out, + unsigned int size) const + { + unsigned int initial_size = size; + // By default, start at the first bit of the first page of values. + unsigned int start_page = 0; + unsigned int start_page_value = 0; + if (unlikely (codepoint != INVALID)) + { + const auto* page_map_array = page_map.arrayZ; + unsigned int major = get_major (codepoint); + unsigned int i = last_page_lookup; + if (unlikely (i >= page_map.length || page_map_array[i].major != major)) + { + page_map.bfind(major, &i, HB_NOT_FOUND_STORE_CLOSEST); + if (unlikely (i >= page_map.length)) + { + // codepoint is greater than our max element. + while (++codepoint != INVALID && size) + { + *out++ = codepoint; + size--; + } + return initial_size - size; + } + } + start_page = i; + start_page_value = page_remainder (codepoint + 1); + if (unlikely (start_page_value == 0)) + { + // The export-after value was last in the page. Start on next page. + start_page++; + start_page_value = 0; + } + } + + hb_codepoint_t next_value = codepoint + 1; + for (unsigned int i=start_page; i= 0; i--) + { + const auto& map = page_map[(unsigned) i]; + const auto& page = pages[map.index]; + + if (!page.is_empty ()) + return map.major * page_t::PAGE_BITS + page.get_max (); + } + return INVALID; + } + + static constexpr hb_codepoint_t INVALID = page_t::INVALID; + + /* + * Iterator implementation. + */ + struct iter_t : hb_iter_with_fallback_t + { + static constexpr bool is_sorted_iterator = true; + static constexpr bool has_fast_len = true; + iter_t (const hb_bit_set_t &s_ = Null (hb_bit_set_t), + bool init = true) : s (&s_), v (INVALID), l(0) + { + if (init) + { + l = s->get_population () + 1; + __next__ (); + } + } + + typedef hb_codepoint_t __item_t__; + hb_codepoint_t __item__ () const { return v; } + bool __more__ () const { return v != INVALID; } + void __next__ () { s->next (&v); if (l) l--; } + void __prev__ () { s->previous (&v); } + unsigned __len__ () const { return l; } + iter_t end () const { return iter_t (*s, false); } + bool operator != (const iter_t& o) const + { return s != o.s || v != o.v; } + + protected: + const hb_bit_set_t *s; + hb_codepoint_t v; + unsigned l; + }; + iter_t iter () const { return iter_t (*this); } + operator iter_t () const { return iter (); } + + protected: + + page_t *page_for (hb_codepoint_t g, bool insert = false) + { + unsigned major = get_major (g); + + /* The extra page_map length is necessary; can't just rely on vector here, + * since the next check would be tricked because a null page also has + * major==0, which we can't distinguish from an actually major==0 page... */ + unsigned i = last_page_lookup; + if (likely (i < page_map.length)) + { + auto &cached_page = page_map.arrayZ[i]; + if (cached_page.major == major) + return &pages.arrayZ[cached_page.index]; + } + + page_map_t map = {major, pages.length}; + if (!page_map.bfind (map, &i, HB_NOT_FOUND_STORE_CLOSEST)) + { + if (!insert) + return nullptr; + + if (unlikely (!resize (pages.length + 1))) + return nullptr; + + pages.arrayZ[map.index].init0 (); + memmove (page_map.arrayZ + i + 1, + page_map.arrayZ + i, + (page_map.length - 1 - i) * page_map.item_size); + page_map.arrayZ[i] = map; + } + + last_page_lookup = i; + return &pages.arrayZ[page_map.arrayZ[i].index]; + } + const page_t *page_for (hb_codepoint_t g) const + { + unsigned major = get_major (g); + + /* The extra page_map length is necessary; can't just rely on vector here, + * since the next check would be tricked because a null page also has + * major==0, which we can't distinguish from an actually major==0 page... */ + unsigned i = last_page_lookup; + if (likely (i < page_map.length)) + { + auto &cached_page = page_map.arrayZ[i]; + if (cached_page.major == major) + return &pages.arrayZ[cached_page.index]; + } + + page_map_t key = {major}; + if (!page_map.bfind (key, &i)) + return nullptr; + + last_page_lookup = i; + return &pages.arrayZ[page_map[i].index]; + } + page_t &page_at (unsigned int i) + { + assert (i < page_map.length); + return pages.arrayZ[page_map.arrayZ[i].index]; + } + const page_t &page_at (unsigned int i) const + { + assert (i < page_map.length); + return pages.arrayZ[page_map.arrayZ[i].index]; + } + unsigned int get_major (hb_codepoint_t g) const { return g >> page_t::PAGE_BITS_LOG_2; } + unsigned int page_remainder (hb_codepoint_t g) const { return g & page_t::PAGE_BITMASK; } + hb_codepoint_t major_start (unsigned int major) const { return major << page_t::PAGE_BITS_LOG_2; } +}; + + +#endif /* HB_BIT_SET_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-blob.cc b/src/3rdparty/harfbuzz-ng/src/hb-blob.cc index 59c83336374..265effba037 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-blob.cc +++ b/src/3rdparty/harfbuzz-ng/src/hb-blob.cc @@ -1,5 +1,6 @@ /* * Copyright © 2009 Red Hat, Inc. + * Copyright © 2018 Ebrahim Byagowi * * This is part of HarfBuzz, a text shaping library. * @@ -24,15 +25,8 @@ * Red Hat Author(s): Behdad Esfahbod */ -/* http://www.oracle.com/technetwork/articles/servers-storage-dev/standardheaderfiles-453865.html */ -#ifndef _POSIX_C_SOURCE -#define _POSIX_C_SOURCE 199309L -#endif - -#include "hb-private.hh" -#include "hb-debug.hh" - -#include "hb-object-private.hh" +#include "hb.hh" +#include "hb-blob.hh" #ifdef HAVE_SYS_MMAN_H #ifdef HAVE_UNISTD_H @@ -41,36 +35,19 @@ #include #endif /* HAVE_SYS_MMAN_H */ -#include -#include +/** + * SECTION: hb-blob + * @title: hb-blob + * @short_description: Binary data containers + * @include: hb.h + * + * Blobs wrap a chunk of binary data to handle lifecycle management of data + * while it is passed between client and HarfBuzz. Blobs are primarily used + * to create font faces, but also to access font face tables, as well as + * pass around other binary data. + **/ -struct hb_blob_t { - hb_object_header_t header; - ASSERT_POD (); - - bool immutable; - - const char *data; - unsigned int length; - hb_memory_mode_t mode; - - void *user_data; - hb_destroy_func_t destroy; -}; - - -static bool _try_writable (hb_blob_t *blob); - -static void -_hb_blob_destroy_user_data (hb_blob_t *blob) -{ - if (blob->destroy) { - blob->destroy (blob->user_data); - blob->user_data = nullptr; - blob->destroy = nullptr; - } -} /** * hb_blob_create: (skip) @@ -78,7 +55,7 @@ _hb_blob_destroy_user_data (hb_blob_t *blob) * @length: Length of @data in bytes. * @mode: Memory mode for @data. * @user_data: Data parameter to pass to @destroy. - * @destroy: Callback to call when @data is not needed anymore. + * @destroy: (nullable): Callback to call when @data is not needed anymore. * * Creates a new "blob" object wrapping @data. The @mode parameter is used * to negotiate ownership and lifecycle of @data. @@ -95,16 +72,54 @@ hb_blob_create (const char *data, void *user_data, hb_destroy_func_t destroy) { - hb_blob_t *blob; - - if (!length || - length >= 1u << 31 || - !(blob = hb_object_create ())) { + if (!length) + { if (destroy) destroy (user_data); return hb_blob_get_empty (); } + hb_blob_t *blob = hb_blob_create_or_fail (data, length, mode, + user_data, destroy); + return likely (blob) ? blob : hb_blob_get_empty (); +} + +/** + * hb_blob_create_or_fail: (skip) + * @data: Pointer to blob data. + * @length: Length of @data in bytes. + * @mode: Memory mode for @data. + * @user_data: Data parameter to pass to @destroy. + * @destroy: (nullable): Callback to call when @data is not needed anymore. + * + * Creates a new "blob" object wrapping @data. The @mode parameter is used + * to negotiate ownership and lifecycle of @data. + * + * Note that this function returns a freshly-allocated empty blob even if @length + * is zero. This is in contrast to hb_blob_create(), which returns the singleton + * empty blob (as returned by hb_blob_get_empty()) if @length is zero. + * + * Return value: New blob, or `NULL` if failed. Destroy with hb_blob_destroy(). + * + * Since: 2.8.2 + **/ +hb_blob_t * +hb_blob_create_or_fail (const char *data, + unsigned int length, + hb_memory_mode_t mode, + void *user_data, + hb_destroy_func_t destroy) +{ + hb_blob_t *blob; + + if (length >= 1u << 31 || + !(blob = hb_object_create ())) + { + if (destroy) + destroy (user_data); + return nullptr; + } + blob->data = data; blob->length = length; blob->mode = mode; @@ -114,9 +129,10 @@ hb_blob_create (const char *data, if (blob->mode == HB_MEMORY_MODE_DUPLICATE) { blob->mode = HB_MEMORY_MODE_READONLY; - if (!_try_writable (blob)) { + if (!blob->try_make_writable ()) + { hb_blob_destroy (blob); - return hb_blob_get_empty (); + return nullptr; } } @@ -136,7 +152,7 @@ _hb_blob_destroy (void *data) * @length: Length of sub-blob. * * Returns a blob that represents a range of bytes in @parent. The new - * blob is always created with %HB_MEMORY_MODE_READONLY, meaning that it + * blob is always created with #HB_MEMORY_MODE_READONLY, meaning that it * will never modify data in the parent blob. The parent data is not * expected to be modified, and will result in undefined behavior if it * is. @@ -156,13 +172,13 @@ hb_blob_create_sub_blob (hb_blob_t *parent, { hb_blob_t *blob; - if (!length || offset >= parent->length) + if (!length || !parent || offset >= parent->length) return hb_blob_get_empty (); hb_blob_make_immutable (parent); blob = hb_blob_create (parent->data + offset, - MIN (length, parent->length - offset), + hb_min (length, parent->length - offset), HB_MEMORY_MODE_READONLY, hb_blob_reference (parent), _hb_blob_destroy); @@ -170,6 +186,31 @@ hb_blob_create_sub_blob (hb_blob_t *parent, return blob; } +/** + * hb_blob_copy_writable_or_fail: + * @blob: A blob. + * + * Makes a writable copy of @blob. + * + * Return value: The new blob, or nullptr if allocation failed + * + * Since: 1.8.0 + **/ +hb_blob_t * +hb_blob_copy_writable_or_fail (hb_blob_t *blob) +{ + blob = hb_blob_create (blob->data, + blob->length, + HB_MEMORY_MODE_DUPLICATE, + nullptr, + nullptr); + + if (unlikely (blob == hb_blob_get_empty ())) + blob = nullptr; + + return blob; +} + /** * hb_blob_get_empty: * @@ -177,27 +218,14 @@ hb_blob_create_sub_blob (hb_blob_t *parent, * * See TODO:link object types for more information. * - * Return value: (transfer full): the empty blob. + * Return value: (transfer full): The empty blob. * * Since: 0.9.2 **/ hb_blob_t * -hb_blob_get_empty (void) +hb_blob_get_empty () { - static const hb_blob_t _hb_blob_nil = { - HB_OBJECT_HEADER_STATIC, - - true, /* immutable */ - - nullptr, /* data */ - 0, /* length */ - HB_MEMORY_MODE_READONLY, /* mode */ - - nullptr, /* user_data */ - nullptr /* destroy */ - }; - - return const_cast (&_hb_blob_nil); + return const_cast (&Null (hb_blob_t)); } /** @@ -222,7 +250,7 @@ hb_blob_reference (hb_blob_t *blob) * hb_blob_destroy: (skip) * @blob: a blob. * - * Descreases the reference count on @blob, and if it reaches zero, destroys + * Decreases the reference count on @blob, and if it reaches zero, destroys * @blob, freeing all memory, possibly calling the destroy-callback the blob * was created for if it has not been called already. * @@ -235,20 +263,20 @@ hb_blob_destroy (hb_blob_t *blob) { if (!hb_object_destroy (blob)) return; - _hb_blob_destroy_user_data (blob); - - free (blob); + hb_free (blob); } /** * hb_blob_set_user_data: (skip) - * @blob: a blob. - * @key: key for data to set. - * @data: data to set. - * @destroy: callback to call when @data is not needed anymore. - * @replace: whether to replace an existing data with the same key. + * @blob: An #hb_blob_t + * @key: The user-data key to set + * @data: A pointer to the user data to set + * @destroy: (nullable): A callback to call when @data is not needed anymore + * @replace: Whether to replace an existing data with the same key * - * Return value: + * Attaches a user-data key/data pair to the specified blob. + * + * Return value: `true` if success, `false` otherwise * * Since: 0.9.2 **/ @@ -264,17 +292,18 @@ hb_blob_set_user_data (hb_blob_t *blob, /** * hb_blob_get_user_data: (skip) - * @blob: a blob. - * @key: key for data to get. + * @blob: a blob + * @key: The user-data key to query * - * + * Fetches the user data associated with the specified key, + * attached to the specified font-functions structure. * - * Return value: (transfer none): + * Return value: (transfer none): A pointer to the user data * * Since: 0.9.2 **/ void * -hb_blob_get_user_data (hb_blob_t *blob, +hb_blob_get_user_data (const hb_blob_t *blob, hb_user_data_key_t *key) { return hb_object_get_user_data (blob, key); @@ -283,35 +312,35 @@ hb_blob_get_user_data (hb_blob_t *blob, /** * hb_blob_make_immutable: - * @blob: a blob. + * @blob: a blob * - * + * Makes a blob immutable. * * Since: 0.9.2 **/ void hb_blob_make_immutable (hb_blob_t *blob) { - if (hb_object_is_inert (blob)) + if (hb_object_is_immutable (blob)) return; - blob->immutable = true; + hb_object_make_immutable (blob); } /** * hb_blob_is_immutable: * @blob: a blob. * - * + * Tests whether a blob is immutable. * - * Return value: TODO + * Return value: `true` if @blob is immutable, `false` otherwise * * Since: 0.9.2 **/ hb_bool_t hb_blob_is_immutable (hb_blob_t *blob) { - return blob->immutable; + return hb_object_is_immutable (blob); } @@ -319,9 +348,9 @@ hb_blob_is_immutable (hb_blob_t *blob) * hb_blob_get_length: * @blob: a blob. * - * + * Fetches the length of a blob's data. * - * Return value: the length of blob data in bytes. + * Return value: the length of @blob data in bytes. * * Since: 0.9.2 **/ @@ -334,11 +363,11 @@ hb_blob_get_length (hb_blob_t *blob) /** * hb_blob_get_data: * @blob: a blob. - * @length: (out): + * @length: (out): The length in bytes of the data retrieved * - * + * Fetches the data from a blob. * - * Returns: (transfer none) (array length=length): + * Returns: (nullable) (transfer none) (array length=length): the byte data of @blob. * * Since: 0.9.2 **/ @@ -363,29 +392,27 @@ hb_blob_get_data (hb_blob_t *blob, unsigned int *length) * fails. * * Returns: (transfer none) (array length=length): Writable blob data, - * or %NULL if failed. + * or `NULL` if failed. * * Since: 0.9.2 **/ char * hb_blob_get_data_writable (hb_blob_t *blob, unsigned int *length) { - if (!_try_writable (blob)) { - if (length) - *length = 0; - + if (hb_object_is_immutable (blob) || + !blob->try_make_writable ()) + { + if (length) *length = 0; return nullptr; } - if (length) - *length = blob->length; - + if (length) *length = blob->length; return const_cast (blob->data); } -static hb_bool_t -_try_make_writable_inplace_unix (hb_blob_t *blob) +bool +hb_blob_t::try_make_writable_inplace_unix () { #if defined(HAVE_SYS_MMAN_H) && defined(HAVE_MPROTECT) uintptr_t pagesize = -1, mask, length; @@ -400,25 +427,25 @@ _try_make_writable_inplace_unix (hb_blob_t *blob) #endif if ((uintptr_t) -1L == pagesize) { - DEBUG_MSG_FUNC (BLOB, blob, "failed to get pagesize: %s", strerror (errno)); + DEBUG_MSG_FUNC (BLOB, this, "failed to get pagesize: %s", strerror (errno)); return false; } - DEBUG_MSG_FUNC (BLOB, blob, "pagesize is %lu", (unsigned long) pagesize); + DEBUG_MSG_FUNC (BLOB, this, "pagesize is %lu", (unsigned long) pagesize); mask = ~(pagesize-1); - addr = (const char *) (((uintptr_t) blob->data) & mask); - length = (const char *) (((uintptr_t) blob->data + blob->length + pagesize-1) & mask) - addr; - DEBUG_MSG_FUNC (BLOB, blob, + addr = (const char *) (((uintptr_t) this->data) & mask); + length = (const char *) (((uintptr_t) this->data + this->length + pagesize-1) & mask) - addr; + DEBUG_MSG_FUNC (BLOB, this, "calling mprotect on [%p..%p] (%lu bytes)", addr, addr+length, (unsigned long) length); if (-1 == mprotect ((void *) addr, length, PROT_READ | PROT_WRITE)) { - DEBUG_MSG_FUNC (BLOB, blob, "mprotect failed: %s", strerror (errno)); + DEBUG_MSG_FUNC (BLOB, this, "mprotect failed: %s", strerror (errno)); return false; } - blob->mode = HB_MEMORY_MODE_WRITABLE; + this->mode = HB_MEMORY_MODE_WRITABLE; - DEBUG_MSG_FUNC (BLOB, blob, + DEBUG_MSG_FUNC (BLOB, this, "successfully made [%p..%p] (%lu bytes) writable\n", addr, addr+length, (unsigned long) length); return true; @@ -427,53 +454,322 @@ _try_make_writable_inplace_unix (hb_blob_t *blob) #endif } -static bool -_try_writable_inplace (hb_blob_t *blob) +bool +hb_blob_t::try_make_writable_inplace () { - DEBUG_MSG_FUNC (BLOB, blob, "making writable inplace\n"); + DEBUG_MSG_FUNC (BLOB, this, "making writable inplace\n"); - if (_try_make_writable_inplace_unix (blob)) + if (this->try_make_writable_inplace_unix ()) return true; - DEBUG_MSG_FUNC (BLOB, blob, "making writable -> FAILED\n"); + DEBUG_MSG_FUNC (BLOB, this, "making writable -> FAILED\n"); /* Failed to make writable inplace, mark that */ - blob->mode = HB_MEMORY_MODE_READONLY; + this->mode = HB_MEMORY_MODE_READONLY; return false; } -static bool -_try_writable (hb_blob_t *blob) +bool +hb_blob_t::try_make_writable () { - if (blob->immutable) - return false; + if (unlikely (!length)) + mode = HB_MEMORY_MODE_WRITABLE; - if (blob->mode == HB_MEMORY_MODE_WRITABLE) + if (this->mode == HB_MEMORY_MODE_WRITABLE) return true; - if (blob->mode == HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE && _try_writable_inplace (blob)) + if (this->mode == HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE && this->try_make_writable_inplace ()) return true; - if (blob->mode == HB_MEMORY_MODE_WRITABLE) + if (this->mode == HB_MEMORY_MODE_WRITABLE) return true; - DEBUG_MSG_FUNC (BLOB, blob, "current data is -> %p\n", blob->data); + DEBUG_MSG_FUNC (BLOB, this, "current data is -> %p\n", this->data); char *new_data; - new_data = (char *) malloc (blob->length); + new_data = (char *) hb_malloc (this->length); if (unlikely (!new_data)) return false; - DEBUG_MSG_FUNC (BLOB, blob, "dupped successfully -> %p\n", blob->data); + DEBUG_MSG_FUNC (BLOB, this, "dupped successfully -> %p\n", this->data); - memcpy (new_data, blob->data, blob->length); - _hb_blob_destroy_user_data (blob); - blob->mode = HB_MEMORY_MODE_WRITABLE; - blob->data = new_data; - blob->user_data = new_data; - blob->destroy = free; + hb_memcpy (new_data, this->data, this->length); + this->destroy_user_data (); + this->mode = HB_MEMORY_MODE_WRITABLE; + this->data = new_data; + this->user_data = new_data; + this->destroy = hb_free; return true; } + +/* + * Mmap + */ + +#ifndef HB_NO_OPEN +#ifdef HAVE_MMAP +# if !defined(HB_NO_RESOURCE_FORK) && defined(__APPLE__) +# include +# endif +# include +# include +# include +#endif + +#ifdef _WIN32 +# include +#else +# ifndef O_BINARY +# define O_BINARY 0 +# endif +#endif + +#ifndef MAP_NORESERVE +# define MAP_NORESERVE 0 +#endif + +struct hb_mapped_file_t +{ + char *contents; + unsigned long length; +#ifdef _WIN32 + HANDLE mapping; +#endif +}; + +#if (defined(HAVE_MMAP) || defined(_WIN32)) && !defined(HB_NO_MMAP) +static void +_hb_mapped_file_destroy (void *file_) +{ + hb_mapped_file_t *file = (hb_mapped_file_t *) file_; +#ifdef HAVE_MMAP + munmap (file->contents, file->length); +#elif defined(_WIN32) + UnmapViewOfFile (file->contents); + CloseHandle (file->mapping); +#else + assert (0); // If we don't have mmap we shouldn't reach here +#endif + + hb_free (file); +} +#endif + +#ifdef _PATH_RSRCFORKSPEC +static int +_open_resource_fork (const char *file_name, hb_mapped_file_t *file) +{ + size_t name_len = strlen (file_name); + size_t len = name_len + sizeof (_PATH_RSRCFORKSPEC); + + char *rsrc_name = (char *) hb_malloc (len); + if (unlikely (!rsrc_name)) return -1; + + strncpy (rsrc_name, file_name, name_len); + strncpy (rsrc_name + name_len, _PATH_RSRCFORKSPEC, + sizeof (_PATH_RSRCFORKSPEC)); + + int fd = open (rsrc_name, O_RDONLY | O_BINARY, 0); + hb_free (rsrc_name); + + if (fd != -1) + { + struct stat st; + if (fstat (fd, &st) != -1) + file->length = (unsigned long) st.st_size; + else + { + close (fd); + fd = -1; + } + } + + return fd; +} +#endif + +/** + * hb_blob_create_from_file: + * @file_name: A font filename + * + * Creates a new blob containing the data from the + * specified binary font file. + * + * Returns: An #hb_blob_t pointer with the content of the file, + * or hb_blob_get_empty() if failed. + * + * Since: 1.7.7 + **/ +hb_blob_t * +hb_blob_create_from_file (const char *file_name) +{ + hb_blob_t *blob = hb_blob_create_from_file_or_fail (file_name); + return likely (blob) ? blob : hb_blob_get_empty (); +} + +/** + * hb_blob_create_from_file_or_fail: + * @file_name: A font filename + * + * Creates a new blob containing the data from the + * specified binary font file. + * + * Returns: An #hb_blob_t pointer with the content of the file, + * or `NULL` if failed. + * + * Since: 2.8.2 + **/ +hb_blob_t * +hb_blob_create_from_file_or_fail (const char *file_name) +{ + /* Adopted from glib's gmappedfile.c with Matthias Clasen and + Allison Lortie permission but changed a lot to suit our need. */ +#if defined(HAVE_MMAP) && !defined(HB_NO_MMAP) + hb_mapped_file_t *file = (hb_mapped_file_t *) hb_calloc (1, sizeof (hb_mapped_file_t)); + if (unlikely (!file)) return nullptr; + + int fd = open (file_name, O_RDONLY | O_BINARY, 0); + if (unlikely (fd == -1)) goto fail_without_close; + + struct stat st; + if (unlikely (fstat (fd, &st) == -1)) goto fail; + + file->length = (unsigned long) st.st_size; + +#ifdef _PATH_RSRCFORKSPEC + if (unlikely (file->length == 0)) + { + int rfd = _open_resource_fork (file_name, file); + if (rfd != -1) + { + close (fd); + fd = rfd; + } + } +#endif + + file->contents = (char *) mmap (nullptr, file->length, PROT_READ, + MAP_PRIVATE | MAP_NORESERVE, fd, 0); + + if (unlikely (file->contents == MAP_FAILED)) goto fail; + + close (fd); + + return hb_blob_create_or_fail (file->contents, file->length, + HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE, (void *) file, + (hb_destroy_func_t) _hb_mapped_file_destroy); + +fail: + close (fd); +fail_without_close: + hb_free (file); + +#elif defined(_WIN32) && !defined(HB_NO_MMAP) + hb_mapped_file_t *file = (hb_mapped_file_t *) hb_calloc (1, sizeof (hb_mapped_file_t)); + if (unlikely (!file)) return nullptr; + + HANDLE fd; + unsigned int size = strlen (file_name) + 1; + wchar_t * wchar_file_name = (wchar_t *) hb_malloc (sizeof (wchar_t) * size); + if (unlikely (!wchar_file_name)) goto fail_without_close; + mbstowcs (wchar_file_name, file_name, size); +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_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 + hb_free (wchar_file_name); + + if (unlikely (fd == INVALID_HANDLE_VALUE)) goto fail_without_close; + +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_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)) goto fail; + +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_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)) goto fail; + + CloseHandle (fd); + return hb_blob_create_or_fail (file->contents, file->length, + HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE, (void *) file, + (hb_destroy_func_t) _hb_mapped_file_destroy); + +fail: + CloseHandle (fd); +fail_without_close: + hb_free (file); + +#endif + + /* The following tries to read a file without knowing its size beforehand + It's used as a fallback for systems without mmap or to read from pipes */ + unsigned long len = 0, allocated = BUFSIZ * 16; + char *data = (char *) hb_malloc (allocated); + if (unlikely (!data)) return nullptr; + + FILE *fp = fopen (file_name, "rb"); + if (unlikely (!fp)) goto fread_fail_without_close; + + while (!feof (fp)) + { + if (allocated - len < BUFSIZ) + { + allocated *= 2; + /* Don't allocate and go more than ~536MB, our mmap reader still + can cover files like that but lets limit our fallback reader */ + if (unlikely (allocated > (2 << 28))) goto fread_fail; + char *new_data = (char *) hb_realloc (data, allocated); + if (unlikely (!new_data)) goto fread_fail; + data = new_data; + } + + unsigned long addition = fread (data + len, 1, allocated - len, fp); + + int err = ferror (fp); +#ifdef EINTR // armcc doesn't have it + if (unlikely (err == EINTR)) continue; +#endif + if (unlikely (err)) goto fread_fail; + + len += addition; + } + fclose (fp); + + return hb_blob_create_or_fail (data, len, HB_MEMORY_MODE_WRITABLE, data, + (hb_destroy_func_t) hb_free); + +fread_fail: + fclose (fp); +fread_fail_without_close: + hb_free (data); + return nullptr; +} +#endif /* !HB_NO_OPEN */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-blob.h b/src/3rdparty/harfbuzz-ng/src/hb-blob.h index ef3fc98c05d..db50067e164 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-blob.h +++ b/src/3rdparty/harfbuzz-ng/src/hb-blob.h @@ -24,7 +24,7 @@ * Red Hat Author(s): Behdad Esfahbod */ -#ifndef HB_H_IN +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) #error "Include instead." #endif @@ -36,25 +36,36 @@ HB_BEGIN_DECLS -/* - * Note re various memory-modes: +/** + * hb_memory_mode_t: + * @HB_MEMORY_MODE_DUPLICATE: HarfBuzz immediately makes a copy of the data. + * @HB_MEMORY_MODE_READONLY: HarfBuzz client will never modify the data, + * and HarfBuzz will never modify the data. + * @HB_MEMORY_MODE_WRITABLE: HarfBuzz client made a copy of the data solely + * for HarfBuzz, so HarfBuzz may modify the data. + * @HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE: See above + * + * Data type holding the memory modes available to + * client programs. + * + * Regarding these various memory-modes: * * - In no case shall the HarfBuzz client modify memory * that is passed to HarfBuzz in a blob. If there is - * any such possibility, MODE_DUPLICATE should be used + * any such possibility, @HB_MEMORY_MODE_DUPLICATE should be used * such that HarfBuzz makes a copy immediately, * - * - Use MODE_READONLY otherse, unless you really really + * - Use @HB_MEMORY_MODE_READONLY otherwise, unless you really really * really know what you are doing, * - * - MODE_WRITABLE is appropriate if you really made a + * - @HB_MEMORY_MODE_WRITABLE is appropriate if you really made a * copy of data solely for the purpose of passing to * HarfBuzz and doing that just once (no reuse!), * - * - If the font is mmap()ed, it's ok to use - * READONLY_MAY_MAKE_WRITABLE, however, using that mode - * correctly is very tricky. Use MODE_READONLY instead. - */ + * - If the font is mmap()ed, it's okay to use + * @HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE, however, using that mode + * correctly is very tricky. Use @HB_MEMORY_MODE_READONLY instead. + **/ typedef enum { HB_MEMORY_MODE_DUPLICATE, HB_MEMORY_MODE_READONLY, @@ -62,6 +73,14 @@ typedef enum { HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE } hb_memory_mode_t; +/** + * hb_blob_t: + * + * Data type for blobs. A blob wraps a chunk of binary + * data and facilitates its lifecycle management between + * a client program and HarfBuzz. + * + **/ typedef struct hb_blob_t hb_blob_t; HB_EXTERN hb_blob_t * @@ -71,6 +90,19 @@ hb_blob_create (const char *data, void *user_data, hb_destroy_func_t destroy); +HB_EXTERN hb_blob_t * +hb_blob_create_or_fail (const char *data, + unsigned int length, + hb_memory_mode_t mode, + void *user_data, + hb_destroy_func_t destroy); + +HB_EXTERN hb_blob_t * +hb_blob_create_from_file (const char *file_name); + +HB_EXTERN hb_blob_t * +hb_blob_create_from_file_or_fail (const char *file_name); + /* Always creates with MEMORY_MODE_READONLY. * Even if the parent blob is writable, we don't * want the user of the sub-blob to be able to @@ -82,6 +114,9 @@ hb_blob_create_sub_blob (hb_blob_t *parent, unsigned int offset, unsigned int length); +HB_EXTERN hb_blob_t * +hb_blob_copy_writable_or_fail (hb_blob_t *blob); + HB_EXTERN hb_blob_t * hb_blob_get_empty (void); @@ -100,7 +135,7 @@ hb_blob_set_user_data (hb_blob_t *blob, HB_EXTERN void * -hb_blob_get_user_data (hb_blob_t *blob, +hb_blob_get_user_data (const hb_blob_t *blob, hb_user_data_key_t *key); @@ -120,7 +155,6 @@ hb_blob_get_data (hb_blob_t *blob, unsigned int *length); HB_EXTERN char * hb_blob_get_data_writable (hb_blob_t *blob, unsigned int *length); - HB_END_DECLS #endif /* HB_BLOB_H */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-blob.hh b/src/3rdparty/harfbuzz-ng/src/hb-blob.hh new file mode 100644 index 00000000000..b1b3b94d3dd --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-blob.hh @@ -0,0 +1,98 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_BLOB_HH +#define HB_BLOB_HH + +#include "hb.hh" + + +/* + * hb_blob_t + */ + +struct hb_blob_t +{ + ~hb_blob_t () { destroy_user_data (); } + + void destroy_user_data () + { + if (destroy) + { + destroy (user_data); + user_data = nullptr; + destroy = nullptr; + } + } + + HB_INTERNAL bool try_make_writable (); + HB_INTERNAL bool try_make_writable_inplace (); + HB_INTERNAL bool try_make_writable_inplace_unix (); + + hb_bytes_t as_bytes () const { return hb_bytes_t (data, length); } + template + const Type* as () const { return as_bytes ().as (); } + + public: + hb_object_header_t header; + + const char *data = nullptr; + unsigned int length = 0; + hb_memory_mode_t mode = (hb_memory_mode_t) 0; + + void *user_data = nullptr; + hb_destroy_func_t destroy = nullptr; +}; + + +/* + * hb_blob_ptr_t + */ + +template +struct hb_blob_ptr_t +{ + typedef hb_remove_pointer

T; + + hb_blob_ptr_t (hb_blob_t *b_ = nullptr) : b (b_) {} + hb_blob_t * operator = (hb_blob_t *b_) { return b = b_; } + const T * operator -> () const { return get (); } + const T & operator * () const { return *get (); } + template operator const C * () const { return get (); } + operator const char * () const { return (const char *) get (); } + const T * get () const { return b->as (); } + hb_blob_t * get_blob () const { return b.get_raw (); } + unsigned int get_length () const { return b.get ()->length; } + void destroy () { hb_blob_destroy (b.get_raw ()); b = nullptr; } + + private: + hb_nonnull_ptr_t b; +}; + + +#endif /* HB_BLOB_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-buffer-deserialize-json.hh b/src/3rdparty/harfbuzz-ng/src/hb-buffer-deserialize-json.hh index 3f626bda40d..1deaaafd879 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-buffer-deserialize-json.hh +++ b/src/3rdparty/harfbuzz-ng/src/hb-buffer-deserialize-json.hh @@ -29,35 +29,41 @@ #ifndef HB_BUFFER_DESERIALIZE_JSON_HH #define HB_BUFFER_DESERIALIZE_JSON_HH -#include "hb-private.hh" +#include "hb.hh" #line 36 "hb-buffer-deserialize-json.hh" static const unsigned char _deserialize_json_trans_keys[] = { - 0u, 0u, 9u, 123u, 9u, 34u, 97u, 103u, 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, - 48u, 57u, 9u, 125u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, - 9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u, 9u, 125u, - 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 34u, 34u, - 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, - 65u, 122u, 34u, 122u, 9u, 125u, 9u, 125u, 9u, 93u, 9u, 123u, 0u, 0u, 0 + 0u, 0u, 9u, 123u, 9u, 34u, 97u, 117u, 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, + 48u, 57u, 9u, 125u, 9u, 125u, 9u, 93u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, + 48u, 57u, 9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u, + 9u, 125u, 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, + 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, + 9u, 58u, 9u, 57u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 34u, 92u, + 9u, 125u, 34u, 92u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u, + 9u, 123u, 0u, 0u, 0 }; static const char _deserialize_json_key_spans[] = { - 0, 115, 26, 7, 2, 1, 50, 49, - 10, 117, 117, 117, 1, 50, 49, 10, - 117, 117, 1, 1, 50, 49, 117, 117, - 2, 1, 50, 49, 10, 117, 117, 1, - 50, 49, 10, 117, 117, 1, 50, 49, - 58, 89, 117, 117, 85, 115, 0 + 0, 115, 26, 21, 2, 1, 50, 49, + 10, 117, 117, 85, 117, 1, 50, 49, + 10, 117, 117, 1, 1, 50, 49, 117, + 117, 2, 1, 50, 49, 10, 117, 117, + 1, 50, 49, 10, 117, 117, 1, 1, + 50, 49, 117, 117, 1, 50, 49, 59, + 117, 59, 117, 117, 1, 50, 49, 117, + 115, 0 }; static const short _deserialize_json_index_offsets[] = { - 0, 0, 116, 143, 151, 154, 156, 207, - 257, 268, 386, 504, 622, 624, 675, 725, - 736, 854, 972, 974, 976, 1027, 1077, 1195, - 1313, 1316, 1318, 1369, 1419, 1430, 1548, 1666, - 1668, 1719, 1769, 1780, 1898, 2016, 2018, 2069, - 2119, 2178, 2268, 2386, 2504, 2590, 2706 + 0, 0, 116, 143, 165, 168, 170, 221, + 271, 282, 400, 518, 604, 722, 724, 775, + 825, 836, 954, 1072, 1074, 1076, 1127, 1177, + 1295, 1413, 1416, 1418, 1469, 1519, 1530, 1648, + 1766, 1768, 1819, 1869, 1880, 1998, 2116, 2118, + 2120, 2171, 2221, 2339, 2457, 2459, 2510, 2560, + 2620, 2738, 2798, 2916, 3034, 3036, 3087, 3137, + 3255, 3371 }; static const char _deserialize_json_indicies[] = { @@ -79,41 +85,28 @@ static const char _deserialize_json_indicies[] = { 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 4, 1, - 5, 1, 6, 7, 1, 1, 8, 1, - 9, 10, 1, 11, 1, 11, 11, 11, - 11, 11, 1, 1, 1, 1, 1, 1, + 5, 1, 6, 7, 1, 8, 9, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 11, 1, 1, 1, + 1, 1, 1, 1, 10, 1, 11, 12, + 1, 13, 1, 13, 13, 13, 13, 13, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 12, 1, - 12, 12, 12, 12, 12, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 12, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 13, 1, 1, 14, - 15, 15, 15, 15, 15, 15, 15, 15, - 15, 1, 16, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 1, 18, 18, 18, - 18, 18, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 18, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 19, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 13, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 14, 1, 14, 14, + 14, 14, 14, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 14, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 15, 1, 1, 16, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 1, + 18, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 1, 20, 20, 20, 20, 20, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 20, 1, 21, 21, 21, 21, 21, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 21, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 3, 1, + 1, 1, 20, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 21, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -124,57 +117,38 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 22, - 1, 18, 18, 18, 18, 18, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 18, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 19, 1, 1, 1, - 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 20, 1, 23, 1, 23, 23, 23, 23, 23, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 23, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 24, 1, 24, 24, 24, 24, - 24, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 24, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 25, 1, 1, 26, 27, 27, 27, 27, - 27, 27, 27, 27, 27, 1, 28, 29, - 29, 29, 29, 29, 29, 29, 29, 29, - 1, 30, 30, 30, 30, 30, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 30, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 24, 1, 25, + 25, 25, 25, 25, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 25, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 26, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 32, 1, 30, - 30, 30, 30, 30, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 30, 1, + 1, 1, 1, 27, 1, 20, 20, 20, + 20, 20, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 31, 1, 1, 1, 29, 29, - 29, 29, 29, 29, 29, 29, 29, 29, + 1, 1, 1, 1, 20, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 21, 1, 1, 1, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -182,25 +156,27 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 32, 1, 33, 1, 34, - 1, 34, 34, 34, 34, 34, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 22, 1, 28, 1, 28, 28, 28, + 28, 28, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 34, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 28, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 35, 1, 35, 35, 35, 35, - 35, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 29, 1, + 29, 29, 29, 29, 29, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 35, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 29, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 36, 37, 37, 37, 37, - 37, 37, 37, 37, 37, 1, 38, 38, - 38, 38, 38, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 30, 1, 1, 31, + 32, 32, 32, 32, 32, 32, 32, 32, + 32, 1, 33, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 1, 35, 35, 35, + 35, 35, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 38, 1, 1, + 1, 1, 1, 1, 35, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 39, 1, 1, 1, 1, 1, 1, + 36, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -210,14 +186,13 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 40, 1, 38, 38, 38, 38, - 38, 1, 1, 1, 1, 1, 1, 1, + 1, 37, 1, 35, 35, 35, 35, 35, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 38, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 39, - 1, 1, 1, 41, 41, 41, 41, 41, - 41, 41, 41, 41, 41, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 35, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 36, 1, + 1, 1, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -225,26 +200,26 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 40, 1, 42, 43, 1, 44, 1, 44, - 44, 44, 44, 44, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 37, + 1, 38, 1, 39, 1, 39, 39, 39, + 39, 39, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 44, 1, + 1, 1, 1, 1, 39, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 40, 1, + 40, 40, 40, 40, 40, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 45, 1, 45, 45, 45, 45, 45, 1, + 1, 1, 1, 1, 1, 1, 1, 40, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 41, + 42, 42, 42, 42, 42, 42, 42, 42, + 42, 1, 43, 43, 43, 43, 43, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 45, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 46, 1, - 1, 47, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 1, 49, 50, 50, 50, - 50, 50, 50, 50, 50, 50, 1, 51, - 51, 51, 51, 51, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 51, 1, + 1, 43, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 44, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 52, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -253,42 +228,42 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 45, 1, + 43, 43, 43, 43, 43, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 53, 1, 51, 51, 51, - 51, 51, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 43, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 51, 1, 1, 1, + 1, 1, 1, 44, 1, 1, 1, 46, + 46, 46, 46, 46, 46, 46, 46, 46, + 46, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 52, 1, 1, 1, 50, 50, 50, 50, - 50, 50, 50, 50, 50, 50, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 45, 1, 47, 48, + 1, 49, 1, 49, 49, 49, 49, 49, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 53, 1, 54, 1, 54, 54, 54, - 54, 54, 1, 1, 1, 1, 1, 1, + 1, 1, 49, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 54, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 50, 1, 50, 50, + 50, 50, 50, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 55, 1, - 55, 55, 55, 55, 55, 1, 1, 1, + 1, 1, 1, 1, 1, 50, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 55, + 1, 1, 51, 1, 1, 52, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 1, + 54, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 1, 56, 56, 56, 56, 56, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 56, 1, 1, 57, - 58, 58, 58, 58, 58, 58, 58, 58, - 58, 1, 59, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 1, 61, 61, 61, - 61, 61, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 61, 1, 1, 1, + 1, 1, 56, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 57, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 62, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -297,59 +272,42 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 58, + 1, 56, 56, 56, 56, 56, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 63, 1, 61, 61, 61, 61, 61, 1, 1, 1, 1, 1, 1, 1, 1, + 56, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 57, 1, 1, 1, + 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 61, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 62, 1, - 1, 1, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 58, 1, 59, + 1, 59, 59, 59, 59, 59, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 63, - 1, 64, 1, 64, 64, 64, 64, 64, 1, 1, 1, 1, 1, 1, 1, 1, + 59, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 64, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 60, 1, 60, 60, 60, 60, + 60, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 65, 1, 65, 65, - 65, 65, 65, 1, 1, 1, 1, 1, + 1, 1, 1, 60, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 65, 1, 66, + 61, 1, 1, 62, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 1, 64, 65, + 65, 65, 65, 65, 65, 65, 65, 65, + 1, 66, 66, 66, 66, 66, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 67, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 1, - 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 1, 1, 1, 1, 1, 1, - 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 1, 70, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 71, 71, - 1, 71, 71, 71, 71, 71, 71, 71, - 71, 71, 71, 1, 1, 1, 1, 1, - 1, 1, 71, 71, 71, 71, 71, 71, - 71, 71, 71, 71, 71, 71, 71, 71, - 71, 71, 71, 71, 71, 71, 71, 71, - 71, 71, 71, 71, 1, 1, 1, 1, - 71, 1, 71, 71, 71, 71, 71, 71, - 71, 71, 71, 71, 71, 71, 71, 71, - 71, 71, 71, 71, 71, 71, 71, 71, - 71, 71, 71, 71, 1, 72, 72, 72, - 72, 72, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 72, 1, 1, 1, + 66, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 67, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 73, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -358,14 +316,14 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 68, 1, 66, + 66, 66, 66, 66, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 74, 1, 72, 72, 72, 72, 72, + 1, 1, 1, 1, 1, 1, 66, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 67, 1, 1, 1, 65, 65, + 65, 65, 65, 65, 65, 65, 65, 65, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 72, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 73, 1, - 1, 1, 75, 75, 75, 75, 75, 75, - 75, 75, 75, 75, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -373,21 +331,25 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 74, - 1, 76, 76, 76, 76, 76, 1, 1, + 1, 1, 1, 68, 1, 69, 1, 70, + 1, 70, 70, 70, 70, 70, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 76, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 77, 1, 1, 1, + 70, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 71, 1, 71, 71, 71, 71, + 71, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 71, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 72, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 1, 74, 74, + 74, 74, 74, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 78, 1, 0, - 0, 0, 0, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 74, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 0, 1, + 1, 75, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -397,49 +359,182 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 76, 1, 74, 74, 74, 74, + 74, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 74, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 75, + 1, 1, 1, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 2, 1, 1, 0 + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 76, 1, 78, 1, 78, 78, 78, 78, + 78, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 78, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 79, 1, 79, + 79, 79, 79, 79, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 79, 1, + 80, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 81, 82, + 82, 82, 82, 82, 82, 82, 82, 82, + 1, 84, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 85, 83, 86, 86, 86, + 86, 86, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 86, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 87, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 88, 1, 83, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 83, 1, 89, + 89, 89, 89, 89, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 89, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 90, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 91, 1, 89, 89, 89, + 89, 89, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 89, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 90, 1, 1, 1, 92, 92, 92, 92, + 92, 92, 92, 92, 92, 92, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 91, 1, 93, 1, 93, 93, 93, + 93, 93, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 93, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 94, 1, + 94, 94, 94, 94, 94, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 94, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 95, + 96, 96, 96, 96, 96, 96, 96, 96, + 96, 1, 89, 89, 89, 89, 89, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 89, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 90, 1, 1, + 1, 97, 97, 97, 97, 97, 97, 97, + 97, 97, 97, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 91, 1, + 0, 0, 0, 0, 0, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 2, 1, 1, 0 }; static const char _deserialize_json_trans_targs[] = { - 1, 0, 2, 2, 3, 4, 18, 24, - 37, 5, 12, 6, 7, 8, 9, 11, - 9, 11, 10, 2, 44, 10, 44, 13, - 14, 15, 16, 17, 16, 17, 10, 2, - 44, 19, 20, 21, 22, 23, 10, 2, - 44, 23, 25, 31, 26, 27, 28, 29, - 30, 29, 30, 10, 2, 44, 32, 33, - 34, 35, 36, 35, 36, 10, 2, 44, - 38, 39, 40, 42, 43, 41, 10, 41, - 10, 2, 44, 43, 44, 45, 46 + 1, 0, 2, 2, 3, 4, 19, 25, + 38, 44, 52, 5, 13, 6, 7, 8, + 9, 12, 9, 12, 10, 2, 11, 10, + 11, 11, 56, 57, 14, 15, 16, 17, + 18, 17, 18, 10, 2, 11, 20, 21, + 22, 23, 24, 10, 2, 11, 24, 26, + 32, 27, 28, 29, 30, 31, 30, 31, + 10, 2, 11, 33, 34, 35, 36, 37, + 36, 37, 10, 2, 11, 39, 40, 41, + 42, 43, 10, 2, 11, 43, 45, 46, + 47, 50, 51, 47, 48, 49, 10, 2, + 11, 10, 2, 11, 51, 53, 54, 50, + 55, 55 }; static const char _deserialize_json_trans_actions[] = { 0, 0, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 2, 2, 2, - 0, 0, 3, 3, 4, 0, 5, 0, - 0, 2, 2, 2, 0, 0, 6, 6, - 7, 0, 0, 0, 2, 2, 8, 8, - 9, 0, 0, 0, 0, 0, 2, 2, - 2, 0, 0, 10, 10, 11, 0, 0, - 2, 2, 2, 0, 0, 12, 12, 13, - 0, 0, 0, 2, 2, 2, 14, 0, - 15, 15, 16, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 2, + 2, 2, 0, 0, 3, 3, 4, 0, + 5, 0, 0, 0, 0, 0, 2, 2, + 2, 0, 0, 6, 6, 7, 0, 0, + 0, 2, 2, 8, 8, 9, 0, 0, + 0, 0, 0, 2, 2, 2, 0, 0, + 10, 10, 11, 0, 0, 2, 2, 2, + 0, 0, 12, 12, 13, 0, 0, 0, + 2, 2, 14, 14, 15, 0, 0, 0, + 2, 16, 16, 0, 17, 0, 18, 18, + 19, 20, 20, 21, 17, 0, 0, 22, + 22, 23 }; static const int deserialize_json_start = 1; -static const int deserialize_json_first_final = 44; +static const int deserialize_json_first_final = 56; static const int deserialize_json_error = 0; static const int deserialize_json_en_main = 1; -#line 97 "hb-buffer-deserialize-json.rl" +#line 111 "hb-buffer-deserialize-json.rl" static hb_bool_t -_hb_buffer_deserialize_glyphs_json (hb_buffer_t *buffer, +_hb_buffer_deserialize_json (hb_buffer_t *buffer, const char *buf, unsigned int buf_len, const char **end_ptr, @@ -448,26 +543,24 @@ _hb_buffer_deserialize_glyphs_json (hb_buffer_t *buffer, const char *p = buf, *pe = buf + buf_len; /* Ensure we have positions. */ - (void) hb_buffer_get_glyph_positions (buffer, NULL); + (void) hb_buffer_get_glyph_positions (buffer, nullptr); while (p < pe && ISSPACE (*p)) p++; if (p < pe && *p == (buffer->len ? ',' : '[')) - { *end_ptr = ++p; - } - const char *tok = NULL; + const char *tok = nullptr; int cs; hb_glyph_info_t info = {0}; hb_glyph_position_t pos = {0}; -#line 466 "hb-buffer-deserialize-json.hh" +#line 559 "hb-buffer-deserialize-json.hh" { cs = deserialize_json_start; } -#line 471 "hb-buffer-deserialize-json.hh" +#line 564 "hb-buffer-deserialize-json.hh" { int _slen; int _trans; @@ -495,15 +588,15 @@ _resume: case 1: #line 38 "hb-buffer-deserialize-json.rl" { - memset (&info, 0, sizeof (info)); - memset (&pos , 0, sizeof (pos )); + hb_memset (&info, 0, sizeof (info)); + hb_memset (&pos , 0, sizeof (pos )); } break; case 5: #line 43 "hb-buffer-deserialize-json.rl" { buffer->add_info (info); - if (buffer->in_error) + if (unlikely (!buffer->successful)) return false; buffer->pos[buffer->len - 1] = pos; *end_ptr = p; @@ -515,112 +608,171 @@ _resume: tok = p; } break; - case 14: + case 17: #line 55 "hb-buffer-deserialize-json.rl" + { if (unlikely (!buffer->ensure_glyphs ())) return false; } + break; + case 23: +#line 56 "hb-buffer-deserialize-json.rl" + { if (unlikely (!buffer->ensure_unicode ())) return false; } + break; + case 18: +#line 58 "hb-buffer-deserialize-json.rl" { + /* TODO Unescape \" and \\ if found. */ if (!hb_font_glyph_from_string (font, - tok, p - tok, + tok+1, p - tok - 2, /* Skip "" */ &info.codepoint)) return false; } break; - case 15: -#line 62 "hb-buffer-deserialize-json.rl" + case 20: +#line 66 "hb-buffer-deserialize-json.rl" { if (!parse_uint (tok, p, &info.codepoint)) return false; } break; case 8: -#line 63 "hb-buffer-deserialize-json.rl" +#line 67 "hb-buffer-deserialize-json.rl" { if (!parse_uint (tok, p, &info.cluster )) return false; } break; case 10: -#line 64 "hb-buffer-deserialize-json.rl" +#line 68 "hb-buffer-deserialize-json.rl" { if (!parse_int (tok, p, &pos.x_offset )) return false; } break; case 12: -#line 65 "hb-buffer-deserialize-json.rl" +#line 69 "hb-buffer-deserialize-json.rl" { if (!parse_int (tok, p, &pos.y_offset )) return false; } break; case 3: -#line 66 "hb-buffer-deserialize-json.rl" +#line 70 "hb-buffer-deserialize-json.rl" { if (!parse_int (tok, p, &pos.x_advance)) return false; } break; case 6: -#line 67 "hb-buffer-deserialize-json.rl" +#line 71 "hb-buffer-deserialize-json.rl" { if (!parse_int (tok, p, &pos.y_advance)) return false; } break; + case 14: +#line 72 "hb-buffer-deserialize-json.rl" + { if (!parse_uint (tok, p, &info.mask )) return false; } + break; case 16: -#line 62 "hb-buffer-deserialize-json.rl" +#line 51 "hb-buffer-deserialize-json.rl" + { + tok = p; +} +#line 55 "hb-buffer-deserialize-json.rl" + { if (unlikely (!buffer->ensure_glyphs ())) return false; } + break; + case 22: +#line 51 "hb-buffer-deserialize-json.rl" + { + tok = p; +} +#line 56 "hb-buffer-deserialize-json.rl" + { if (unlikely (!buffer->ensure_unicode ())) return false; } + break; + case 19: +#line 58 "hb-buffer-deserialize-json.rl" + { + /* TODO Unescape \" and \\ if found. */ + if (!hb_font_glyph_from_string (font, + tok+1, p - tok - 2, /* Skip "" */ + &info.codepoint)) + return false; +} +#line 43 "hb-buffer-deserialize-json.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 21: +#line 66 "hb-buffer-deserialize-json.rl" { if (!parse_uint (tok, p, &info.codepoint)) return false; } #line 43 "hb-buffer-deserialize-json.rl" { buffer->add_info (info); - if (buffer->in_error) + if (unlikely (!buffer->successful)) return false; buffer->pos[buffer->len - 1] = pos; *end_ptr = p; } break; case 9: -#line 63 "hb-buffer-deserialize-json.rl" +#line 67 "hb-buffer-deserialize-json.rl" { if (!parse_uint (tok, p, &info.cluster )) return false; } #line 43 "hb-buffer-deserialize-json.rl" { buffer->add_info (info); - if (buffer->in_error) + if (unlikely (!buffer->successful)) return false; buffer->pos[buffer->len - 1] = pos; *end_ptr = p; } break; case 11: -#line 64 "hb-buffer-deserialize-json.rl" +#line 68 "hb-buffer-deserialize-json.rl" { if (!parse_int (tok, p, &pos.x_offset )) return false; } #line 43 "hb-buffer-deserialize-json.rl" { buffer->add_info (info); - if (buffer->in_error) + if (unlikely (!buffer->successful)) return false; buffer->pos[buffer->len - 1] = pos; *end_ptr = p; } break; case 13: -#line 65 "hb-buffer-deserialize-json.rl" +#line 69 "hb-buffer-deserialize-json.rl" { if (!parse_int (tok, p, &pos.y_offset )) return false; } #line 43 "hb-buffer-deserialize-json.rl" { buffer->add_info (info); - if (buffer->in_error) + if (unlikely (!buffer->successful)) return false; buffer->pos[buffer->len - 1] = pos; *end_ptr = p; } break; case 4: -#line 66 "hb-buffer-deserialize-json.rl" +#line 70 "hb-buffer-deserialize-json.rl" { if (!parse_int (tok, p, &pos.x_advance)) return false; } #line 43 "hb-buffer-deserialize-json.rl" { buffer->add_info (info); - if (buffer->in_error) + if (unlikely (!buffer->successful)) return false; buffer->pos[buffer->len - 1] = pos; *end_ptr = p; } break; case 7: -#line 67 "hb-buffer-deserialize-json.rl" +#line 71 "hb-buffer-deserialize-json.rl" { if (!parse_int (tok, p, &pos.y_advance)) return false; } #line 43 "hb-buffer-deserialize-json.rl" { buffer->add_info (info); - if (buffer->in_error) + if (unlikely (!buffer->successful)) return false; buffer->pos[buffer->len - 1] = pos; *end_ptr = p; } break; -#line 624 "hb-buffer-deserialize-json.hh" + case 15: +#line 72 "hb-buffer-deserialize-json.rl" + { if (!parse_uint (tok, p, &info.mask )) return false; } +#line 43 "hb-buffer-deserialize-json.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; +#line 776 "hb-buffer-deserialize-json.hh" } _again: @@ -632,7 +784,7 @@ _again: _out: {} } -#line 125 "hb-buffer-deserialize-json.rl" +#line 137 "hb-buffer-deserialize-json.rl" *end_ptr = p; diff --git a/src/3rdparty/harfbuzz-ng/src/hb-buffer-deserialize-text-glyphs.hh b/src/3rdparty/harfbuzz-ng/src/hb-buffer-deserialize-text-glyphs.hh new file mode 100644 index 00000000000..ea81273b31c --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-buffer-deserialize-text-glyphs.hh @@ -0,0 +1,692 @@ + +#line 1 "hb-buffer-deserialize-text-glyphs.rl" +/* + * Copyright © 2013 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): Behdad Esfahbod + */ + +#ifndef HB_BUFFER_DESERIALIZE_TEXT_GLYPHS_HH +#define HB_BUFFER_DESERIALIZE_TEXT_GLYPHS_HH + +#include "hb.hh" + + +#line 36 "hb-buffer-deserialize-text-glyphs.hh" +static const unsigned char _deserialize_text_glyphs_trans_keys[] = { + 0u, 0u, 48u, 57u, 45u, 57u, 48u, 57u, 45u, 57u, 48u, 57u, 48u, 57u, 45u, 57u, + 48u, 57u, 44u, 44u, 45u, 57u, 48u, 57u, 44u, 57u, 43u, 124u, 9u, 124u, 9u, 124u, + 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, + 9u, 124u, 9u, 124u, 9u, 124u, 0 +}; + +static const char _deserialize_text_glyphs_key_spans[] = { + 0, 10, 13, 10, 13, 10, 10, 13, + 10, 1, 13, 10, 14, 82, 116, 116, + 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116 +}; + +static const short _deserialize_text_glyphs_index_offsets[] = { + 0, 0, 11, 25, 36, 50, 61, 72, + 86, 97, 99, 113, 124, 139, 222, 339, + 456, 573, 690, 807, 924, 1041, 1158, 1275, + 1392, 1509, 1626 +}; + +static const char _deserialize_text_glyphs_indicies[] = { + 0, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 1, 3, 1, 1, 4, + 5, 5, 5, 5, 5, 5, 5, 5, + 5, 1, 6, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 1, 8, 1, 1, + 9, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 1, 11, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 1, 13, 14, + 14, 14, 14, 14, 14, 14, 14, 14, + 1, 15, 1, 1, 16, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 1, 18, + 19, 19, 19, 19, 19, 19, 19, 19, + 19, 1, 20, 1, 21, 1, 1, 22, + 23, 23, 23, 23, 23, 23, 23, 23, + 23, 1, 24, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 1, 20, 1, 1, + 1, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 1, 26, 26, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 26, 1, + 1, 26, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 26, 26, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 26, 1, 28, + 28, 28, 28, 28, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 28, 27, + 27, 29, 27, 27, 27, 27, 27, 27, + 27, 30, 1, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 31, 27, 27, 32, 27, + 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 33, 1, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 28, 27, 34, 34, 34, 34, + 34, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 34, 26, 26, 35, 26, + 26, 26, 26, 26, 26, 26, 36, 1, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 37, 26, 26, 38, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 39, + 1, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 40, + 26, 41, 41, 41, 41, 41, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 41, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 42, 1, 43, 43, + 43, 43, 43, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 43, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 44, 1, 41, 41, 41, 41, 41, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 41, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 42, 1, + 46, 46, 46, 46, 46, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 46, + 1, 1, 47, 1, 1, 1, 1, 1, + 1, 1, 1, 48, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 49, 1, 50, 50, 50, + 50, 50, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 50, 1, 1, 51, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 52, 1, 50, 50, 50, 50, 50, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 50, 1, 1, 51, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 52, 1, 46, + 46, 46, 46, 46, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 46, 1, + 1, 47, 1, 1, 1, 1, 1, 1, + 1, 1, 48, 1, 1, 1, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 49, 1, 53, 53, 53, 53, + 53, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 53, 1, 1, 54, 1, + 1, 1, 1, 1, 1, 1, 55, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 56, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 57, + 1, 58, 58, 58, 58, 58, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 58, 1, 1, 59, 1, 1, 1, 1, + 1, 1, 1, 60, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 61, 1, 58, 58, + 58, 58, 58, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 58, 1, 1, + 59, 1, 1, 1, 1, 1, 1, 1, + 60, 1, 1, 1, 1, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 61, 1, 53, 53, 53, 53, 53, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 53, 1, 1, 54, 1, 1, + 1, 1, 1, 1, 1, 55, 1, 1, + 1, 1, 62, 62, 62, 62, 62, 62, + 62, 62, 62, 62, 1, 1, 1, 1, + 1, 1, 56, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 57, 1, + 0 +}; + +static const char _deserialize_text_glyphs_trans_targs[] = { + 16, 0, 18, 3, 19, 22, 19, 22, + 5, 20, 21, 20, 21, 23, 26, 8, + 9, 12, 9, 12, 10, 11, 24, 25, + 24, 25, 15, 15, 14, 1, 2, 6, + 7, 13, 15, 1, 2, 6, 7, 13, + 14, 17, 14, 17, 14, 18, 17, 1, + 4, 14, 17, 1, 14, 17, 1, 2, + 7, 14, 17, 1, 2, 14, 26 +}; + +static const char _deserialize_text_glyphs_trans_actions[] = { + 1, 0, 1, 1, 1, 1, 0, 0, + 1, 1, 1, 0, 0, 1, 1, 1, + 1, 1, 0, 0, 2, 1, 1, 1, + 0, 0, 0, 4, 3, 5, 5, 5, + 5, 4, 6, 7, 7, 7, 7, 0, + 6, 8, 8, 0, 0, 0, 9, 10, + 10, 9, 11, 12, 11, 13, 14, 14, + 14, 13, 15, 16, 16, 15, 0 +}; + +static const char _deserialize_text_glyphs_eof_actions[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 3, 6, + 8, 0, 8, 9, 11, 11, 9, 13, + 15, 15, 13 +}; + +static const int deserialize_text_glyphs_start = 14; +static const int deserialize_text_glyphs_first_final = 14; +static const int deserialize_text_glyphs_error = 0; + +static const int deserialize_text_glyphs_en_main = 14; + + +#line 98 "hb-buffer-deserialize-text-glyphs.rl" + + +static hb_bool_t +_hb_buffer_deserialize_text_glyphs (hb_buffer_t *buffer, + const char *buf, + unsigned int buf_len, + const char **end_ptr, + hb_font_t *font) +{ + const char *p = buf, *pe = buf + buf_len, *eof = pe, *orig_pe = pe; + + /* Ensure we have positions. */ + (void) hb_buffer_get_glyph_positions (buffer, nullptr); + + while (p < pe && ISSPACE (*p)) + p++; + if (p < pe && *p == (buffer->len ? '|' : '[')) + *end_ptr = ++p; + + const char *end = strchr ((char *) p, ']'); + if (end) + pe = eof = end; + else + { + end = strrchr ((char *) p, '|'); + if (end) + pe = eof = end; + else + pe = eof = p; + } + + const char *tok = nullptr; + int cs; + hb_glyph_info_t info = {0}; + hb_glyph_position_t pos = {0}; + +#line 353 "hb-buffer-deserialize-text-glyphs.hh" + { + cs = deserialize_text_glyphs_start; + } + +#line 358 "hb-buffer-deserialize-text-glyphs.hh" + { + int _slen; + int _trans; + const unsigned char *_keys; + const char *_inds; + if ( p == pe ) + goto _test_eof; + if ( cs == 0 ) + goto _out; +_resume: + _keys = _deserialize_text_glyphs_trans_keys + (cs<<1); + _inds = _deserialize_text_glyphs_indicies + _deserialize_text_glyphs_index_offsets[cs]; + + _slen = _deserialize_text_glyphs_key_spans[cs]; + _trans = _inds[ _slen > 0 && _keys[0] <=(*p) && + (*p) <= _keys[1] ? + (*p) - _keys[0] : _slen ]; + + cs = _deserialize_text_glyphs_trans_targs[_trans]; + + if ( _deserialize_text_glyphs_trans_actions[_trans] == 0 ) + goto _again; + + switch ( _deserialize_text_glyphs_trans_actions[_trans] ) { + case 1: +#line 51 "hb-buffer-deserialize-text-glyphs.rl" + { + tok = p; +} + break; + case 7: +#line 55 "hb-buffer-deserialize-text-glyphs.rl" + { + /* TODO Unescape delimiters. */ + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} + break; + case 14: +#line 63 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_uint (tok, p, &info.cluster )) return false; } + break; + case 2: +#line 64 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_int (tok, p, &pos.x_offset )) return false; } + break; + case 16: +#line 65 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_int (tok, p, &pos.y_offset )) return false; } + break; + case 10: +#line 66 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_int (tok, p, &pos.x_advance)) return false; } + break; + case 12: +#line 67 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_int (tok, p, &pos.y_advance)) return false; } + break; + case 4: +#line 38 "hb-buffer-deserialize-text-glyphs.rl" + { + hb_memset (&info, 0, sizeof (info)); + hb_memset (&pos , 0, sizeof (pos )); +} +#line 51 "hb-buffer-deserialize-text-glyphs.rl" + { + tok = p; +} + break; + case 6: +#line 55 "hb-buffer-deserialize-text-glyphs.rl" + { + /* TODO Unescape delimiters. */ + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 13: +#line 63 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_uint (tok, p, &info.cluster )) return false; } +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 15: +#line 65 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_int (tok, p, &pos.y_offset )) return false; } +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 9: +#line 66 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_int (tok, p, &pos.x_advance)) return false; } +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 11: +#line 67 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_int (tok, p, &pos.y_advance)) return false; } +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 8: +#line 68 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_uint (tok, p, &info.mask )) return false; } +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 5: +#line 38 "hb-buffer-deserialize-text-glyphs.rl" + { + hb_memset (&info, 0, sizeof (info)); + hb_memset (&pos , 0, sizeof (pos )); +} +#line 51 "hb-buffer-deserialize-text-glyphs.rl" + { + tok = p; +} +#line 55 "hb-buffer-deserialize-text-glyphs.rl" + { + /* TODO Unescape delimiters. */ + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} + break; + case 3: +#line 38 "hb-buffer-deserialize-text-glyphs.rl" + { + hb_memset (&info, 0, sizeof (info)); + hb_memset (&pos , 0, sizeof (pos )); +} +#line 51 "hb-buffer-deserialize-text-glyphs.rl" + { + tok = p; +} +#line 55 "hb-buffer-deserialize-text-glyphs.rl" + { + /* TODO Unescape delimiters. */ + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; +#line 554 "hb-buffer-deserialize-text-glyphs.hh" + } + +_again: + if ( cs == 0 ) + goto _out; + if ( ++p != pe ) + goto _resume; + _test_eof: {} + if ( p == eof ) + { + switch ( _deserialize_text_glyphs_eof_actions[cs] ) { + case 6: +#line 55 "hb-buffer-deserialize-text-glyphs.rl" + { + /* TODO Unescape delimiters. */ + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 13: +#line 63 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_uint (tok, p, &info.cluster )) return false; } +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 15: +#line 65 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_int (tok, p, &pos.y_offset )) return false; } +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 9: +#line 66 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_int (tok, p, &pos.x_advance)) return false; } +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 11: +#line 67 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_int (tok, p, &pos.y_advance)) return false; } +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 8: +#line 68 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_uint (tok, p, &info.mask )) return false; } +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 3: +#line 38 "hb-buffer-deserialize-text-glyphs.rl" + { + hb_memset (&info, 0, sizeof (info)); + hb_memset (&pos , 0, sizeof (pos )); +} +#line 51 "hb-buffer-deserialize-text-glyphs.rl" + { + tok = p; +} +#line 55 "hb-buffer-deserialize-text-glyphs.rl" + { + /* TODO Unescape delimiters. */ + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; +#line 671 "hb-buffer-deserialize-text-glyphs.hh" + } + } + + _out: {} + } + +#line 136 "hb-buffer-deserialize-text-glyphs.rl" + + + if (pe < orig_pe && *pe == ']') + { + pe++; + if (p == pe) + p++; + } + + *end_ptr = p; + + return p == pe; +} + +#endif /* HB_BUFFER_DESERIALIZE_TEXT_GLYPHS_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-buffer-deserialize-text-unicode.hh b/src/3rdparty/harfbuzz-ng/src/hb-buffer-deserialize-text-unicode.hh new file mode 100644 index 00000000000..a8cdf67e73e --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-buffer-deserialize-text-unicode.hh @@ -0,0 +1,332 @@ + +#line 1 "hb-buffer-deserialize-text-unicode.rl" +/* + * Copyright © 2013 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): Behdad Esfahbod + */ + +#ifndef HB_BUFFER_DESERIALIZE_TEXT_UNICODE_HH +#define HB_BUFFER_DESERIALIZE_TEXT_UNICODE_HH + +#include "hb.hh" + + +#line 36 "hb-buffer-deserialize-text-unicode.hh" +static const unsigned char _deserialize_text_unicode_trans_keys[] = { + 0u, 0u, 9u, 117u, 43u, 102u, 48u, 102u, 48u, 57u, 9u, 124u, 9u, 124u, 9u, 124u, + 9u, 124u, 0 +}; + +static const char _deserialize_text_unicode_key_spans[] = { + 0, 109, 60, 55, 10, 116, 116, 116, + 116 +}; + +static const short _deserialize_text_unicode_index_offsets[] = { + 0, 0, 110, 171, 227, 238, 355, 472, + 589 +}; + +static const char _deserialize_text_unicode_indicies[] = { + 0, 0, 0, 0, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 2, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 2, 1, 3, + 1, 1, 1, 1, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 1, 1, + 1, 1, 1, 1, 1, 4, 4, 4, + 4, 4, 4, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 4, 4, 4, + 4, 4, 4, 1, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 1, 1, + 1, 1, 1, 1, 1, 4, 4, 4, + 4, 4, 4, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 4, 4, 4, + 4, 4, 4, 1, 5, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 1, 7, + 7, 7, 7, 7, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 7, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 1, 1, 1, 9, 1, 1, 1, 8, + 8, 8, 8, 8, 8, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 8, + 8, 8, 8, 8, 8, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 10, 1, 11, 11, 11, 11, + 11, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 11, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 0, + 1, 12, 12, 12, 12, 12, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 12, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 13, 1, 12, 12, + 12, 12, 12, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 12, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 13, 1, 0 +}; + +static const char _deserialize_text_unicode_trans_targs[] = { + 1, 0, 2, 3, 5, 7, 8, 6, + 5, 4, 1, 6, 6, 1, 8 +}; + +static const char _deserialize_text_unicode_trans_actions[] = { + 0, 0, 1, 0, 2, 2, 2, 3, + 0, 4, 3, 0, 5, 5, 0 +}; + +static const char _deserialize_text_unicode_eof_actions[] = { + 0, 0, 0, 0, 0, 3, 0, 5, + 5 +}; + +static const int deserialize_text_unicode_start = 1; +static const int deserialize_text_unicode_first_final = 5; +static const int deserialize_text_unicode_error = 0; + +static const int deserialize_text_unicode_en_main = 1; + + +#line 79 "hb-buffer-deserialize-text-unicode.rl" + + +static hb_bool_t +_hb_buffer_deserialize_text_unicode (hb_buffer_t *buffer, + const char *buf, + unsigned int buf_len, + const char **end_ptr, + hb_font_t *font) +{ + const char *p = buf, *pe = buf + buf_len, *eof = pe, *orig_pe = pe; + + while (p < pe && ISSPACE (*p)) + p++; + if (p < pe && *p == (buffer->len ? '|' : '<')) + *end_ptr = ++p; + + const char *end = strchr ((char *) p, '>'); + if (end) + pe = eof = end; + else + { + end = strrchr ((char *) p, '|'); + if (end) + pe = eof = end; + else + pe = eof = p; + } + + + const char *tok = nullptr; + int cs; + hb_glyph_info_t info = {0}; + const hb_glyph_position_t pos = {0}; + +#line 201 "hb-buffer-deserialize-text-unicode.hh" + { + cs = deserialize_text_unicode_start; + } + +#line 206 "hb-buffer-deserialize-text-unicode.hh" + { + int _slen; + int _trans; + const unsigned char *_keys; + const char *_inds; + if ( p == pe ) + goto _test_eof; + if ( cs == 0 ) + goto _out; +_resume: + _keys = _deserialize_text_unicode_trans_keys + (cs<<1); + _inds = _deserialize_text_unicode_indicies + _deserialize_text_unicode_index_offsets[cs]; + + _slen = _deserialize_text_unicode_key_spans[cs]; + _trans = _inds[ _slen > 0 && _keys[0] <=(*p) && + (*p) <= _keys[1] ? + (*p) - _keys[0] : _slen ]; + + cs = _deserialize_text_unicode_trans_targs[_trans]; + + if ( _deserialize_text_unicode_trans_actions[_trans] == 0 ) + goto _again; + + switch ( _deserialize_text_unicode_trans_actions[_trans] ) { + case 1: +#line 38 "hb-buffer-deserialize-text-unicode.rl" + { + hb_memset (&info, 0, sizeof (info)); +} + break; + case 2: +#line 51 "hb-buffer-deserialize-text-unicode.rl" + { + tok = p; +} + break; + case 4: +#line 55 "hb-buffer-deserialize-text-unicode.rl" + {if (!parse_hex (tok, p, &info.codepoint )) return false; } + break; + case 3: +#line 55 "hb-buffer-deserialize-text-unicode.rl" + {if (!parse_hex (tok, p, &info.codepoint )) return false; } +#line 42 "hb-buffer-deserialize-text-unicode.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + if (buffer->have_positions) + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 5: +#line 57 "hb-buffer-deserialize-text-unicode.rl" + { if (!parse_uint (tok, p, &info.cluster )) return false; } +#line 42 "hb-buffer-deserialize-text-unicode.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + if (buffer->have_positions) + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; +#line 273 "hb-buffer-deserialize-text-unicode.hh" + } + +_again: + if ( cs == 0 ) + goto _out; + if ( ++p != pe ) + goto _resume; + _test_eof: {} + if ( p == eof ) + { + switch ( _deserialize_text_unicode_eof_actions[cs] ) { + case 3: +#line 55 "hb-buffer-deserialize-text-unicode.rl" + {if (!parse_hex (tok, p, &info.codepoint )) return false; } +#line 42 "hb-buffer-deserialize-text-unicode.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + if (buffer->have_positions) + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 5: +#line 57 "hb-buffer-deserialize-text-unicode.rl" + { if (!parse_uint (tok, p, &info.cluster )) return false; } +#line 42 "hb-buffer-deserialize-text-unicode.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + if (buffer->have_positions) + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; +#line 311 "hb-buffer-deserialize-text-unicode.hh" + } + } + + _out: {} + } + +#line 115 "hb-buffer-deserialize-text-unicode.rl" + + + if (pe < orig_pe && *pe == '>') + { + pe++; + if (p == pe) + p++; + } + + *end_ptr = p; + + return p == pe; +} + +#endif /* HB_BUFFER_DESERIALIZE_TEXT_UNICODE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-buffer-deserialize-text.hh b/src/3rdparty/harfbuzz-ng/src/hb-buffer-deserialize-text.hh deleted file mode 100644 index d2d8daae7ed..00000000000 --- a/src/3rdparty/harfbuzz-ng/src/hb-buffer-deserialize-text.hh +++ /dev/null @@ -1,571 +0,0 @@ - -#line 1 "hb-buffer-deserialize-text.rl" -/* - * Copyright © 2013 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): Behdad Esfahbod - */ - -#ifndef HB_BUFFER_DESERIALIZE_TEXT_HH -#define HB_BUFFER_DESERIALIZE_TEXT_HH - -#include "hb-private.hh" - - -#line 36 "hb-buffer-deserialize-text.hh" -static const unsigned char _deserialize_text_trans_keys[] = { - 0u, 0u, 9u, 122u, 45u, 57u, 48u, 57u, 45u, 57u, 48u, 57u, 48u, 57u, 45u, 57u, - 48u, 57u, 44u, 44u, 45u, 57u, 48u, 57u, 44u, 57u, 9u, 124u, 9u, 124u, 0u, 0u, - 9u, 122u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, - 9u, 124u, 9u, 124u, 9u, 124u, 0 -}; - -static const char _deserialize_text_key_spans[] = { - 0, 114, 13, 10, 13, 10, 10, 13, - 10, 1, 13, 10, 14, 116, 116, 0, - 114, 116, 116, 116, 116, 116, 116, 116, - 116, 116, 116 -}; - -static const short _deserialize_text_index_offsets[] = { - 0, 0, 115, 129, 140, 154, 165, 176, - 190, 201, 203, 217, 228, 243, 360, 477, - 478, 593, 710, 827, 944, 1061, 1178, 1295, - 1412, 1529, 1646 -}; - -static const char _deserialize_text_indicies[] = { - 0, 0, 0, 0, 0, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 0, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 2, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 1, 1, 1, 1, 1, 1, - 1, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 1, 1, 1, 1, 1, - 1, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 1, 5, 1, 1, 6, - 7, 7, 7, 7, 7, 7, 7, 7, - 7, 1, 8, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 1, 10, 1, 1, - 11, 12, 12, 12, 12, 12, 12, 12, - 12, 12, 1, 13, 14, 14, 14, 14, - 14, 14, 14, 14, 14, 1, 15, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 1, 17, 1, 1, 18, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 1, 20, - 21, 21, 21, 21, 21, 21, 21, 21, - 21, 1, 22, 1, 23, 1, 1, 24, - 25, 25, 25, 25, 25, 25, 25, 25, - 25, 1, 26, 27, 27, 27, 27, 27, - 27, 27, 27, 27, 1, 22, 1, 1, - 1, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 1, 28, 28, 28, 28, - 28, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 28, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 29, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 30, 1, 1, 31, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 32, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 33, - 1, 34, 34, 34, 34, 34, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 34, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 35, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 36, 1, 1, 0, - 0, 0, 0, 0, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 0, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 2, 3, - 3, 3, 3, 3, 3, 3, 3, 3, - 1, 1, 1, 1, 1, 1, 1, 4, - 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, - 4, 1, 1, 1, 1, 1, 1, 4, - 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, - 4, 1, 28, 28, 28, 28, 28, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 28, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 29, 1, 1, 1, - 1, 37, 37, 37, 37, 37, 37, 37, - 37, 37, 37, 1, 1, 1, 30, 1, - 1, 31, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 32, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 33, 1, 38, - 38, 38, 38, 38, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 38, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 39, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 40, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 41, 1, 42, 42, 42, 42, - 42, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 42, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 43, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 44, - 1, 42, 42, 42, 42, 42, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 42, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 14, 14, 14, 14, 14, 14, 14, 14, - 14, 14, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 43, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 44, 1, 38, 38, - 38, 38, 38, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 38, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 39, 1, 1, 1, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 40, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 41, 1, 45, 45, 45, 45, 45, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 45, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 46, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 47, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 48, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 49, 1, - 50, 50, 50, 50, 50, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 50, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 51, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 52, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 53, 1, 50, 50, 50, - 50, 50, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 50, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 51, - 1, 1, 1, 1, 27, 27, 27, 27, - 27, 27, 27, 27, 27, 27, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 52, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 53, 1, 45, 45, 45, 45, 45, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 45, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 46, 1, 1, 1, - 1, 54, 54, 54, 54, 54, 54, 54, - 54, 54, 54, 1, 1, 1, 1, 1, - 1, 47, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 48, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 49, 1, 28, - 28, 28, 28, 28, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 28, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 29, 1, 55, 55, 1, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, - 1, 1, 1, 30, 1, 1, 31, 55, - 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, - 55, 1, 1, 32, 1, 55, 1, 55, - 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, - 55, 1, 33, 1, 0 -}; - -static const char _deserialize_text_trans_targs[] = { - 1, 0, 13, 17, 26, 3, 18, 21, - 18, 21, 5, 19, 20, 19, 20, 22, - 25, 8, 9, 12, 9, 12, 10, 11, - 23, 24, 23, 24, 14, 2, 6, 7, - 15, 16, 14, 15, 16, 17, 14, 4, - 15, 16, 14, 15, 16, 14, 2, 7, - 15, 16, 14, 2, 15, 16, 25, 26 -}; - -static const char _deserialize_text_trans_actions[] = { - 0, 0, 1, 1, 1, 2, 2, 2, - 0, 0, 2, 2, 2, 0, 0, 2, - 2, 2, 2, 2, 0, 0, 3, 2, - 2, 2, 0, 0, 4, 5, 5, 5, - 4, 4, 0, 0, 0, 0, 6, 7, - 6, 6, 8, 8, 8, 9, 10, 10, - 9, 9, 11, 12, 11, 11, 0, 0 -}; - -static const char _deserialize_text_eof_actions[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 4, 0, 0, - 0, 4, 6, 8, 8, 6, 9, 11, - 11, 9, 4 -}; - -static const int deserialize_text_start = 1; -static const int deserialize_text_first_final = 13; -static const int deserialize_text_error = 0; - -static const int deserialize_text_en_main = 1; - - -#line 91 "hb-buffer-deserialize-text.rl" - - -static hb_bool_t -_hb_buffer_deserialize_glyphs_text (hb_buffer_t *buffer, - const char *buf, - unsigned int buf_len, - const char **end_ptr, - hb_font_t *font) -{ - const char *p = buf, *pe = buf + buf_len; - - /* Ensure we have positions. */ - (void) hb_buffer_get_glyph_positions (buffer, NULL); - - while (p < pe && ISSPACE (*p)) - p++; - if (p < pe && *p == (buffer->len ? '|' : '[')) - { - *end_ptr = ++p; - } - - const char *eof = pe, *tok = NULL; - int cs; - hb_glyph_info_t info = {0}; - hb_glyph_position_t pos = {0}; - -#line 343 "hb-buffer-deserialize-text.hh" - { - cs = deserialize_text_start; - } - -#line 348 "hb-buffer-deserialize-text.hh" - { - int _slen; - int _trans; - const unsigned char *_keys; - const char *_inds; - if ( p == pe ) - goto _test_eof; - if ( cs == 0 ) - goto _out; -_resume: - _keys = _deserialize_text_trans_keys + (cs<<1); - _inds = _deserialize_text_indicies + _deserialize_text_index_offsets[cs]; - - _slen = _deserialize_text_key_spans[cs]; - _trans = _inds[ _slen > 0 && _keys[0] <=(*p) && - (*p) <= _keys[1] ? - (*p) - _keys[0] : _slen ]; - - cs = _deserialize_text_trans_targs[_trans]; - - if ( _deserialize_text_trans_actions[_trans] == 0 ) - goto _again; - - switch ( _deserialize_text_trans_actions[_trans] ) { - case 2: -#line 51 "hb-buffer-deserialize-text.rl" - { - tok = p; -} - break; - case 5: -#line 55 "hb-buffer-deserialize-text.rl" - { - if (!hb_font_glyph_from_string (font, - tok, p - tok, - &info.codepoint)) - return false; -} - break; - case 10: -#line 62 "hb-buffer-deserialize-text.rl" - { if (!parse_uint (tok, p, &info.cluster )) return false; } - break; - case 3: -#line 63 "hb-buffer-deserialize-text.rl" - { if (!parse_int (tok, p, &pos.x_offset )) return false; } - break; - case 12: -#line 64 "hb-buffer-deserialize-text.rl" - { if (!parse_int (tok, p, &pos.y_offset )) return false; } - break; - case 7: -#line 65 "hb-buffer-deserialize-text.rl" - { if (!parse_int (tok, p, &pos.x_advance)) return false; } - break; - case 1: -#line 38 "hb-buffer-deserialize-text.rl" - { - memset (&info, 0, sizeof (info)); - memset (&pos , 0, sizeof (pos )); -} -#line 51 "hb-buffer-deserialize-text.rl" - { - tok = p; -} - break; - case 4: -#line 55 "hb-buffer-deserialize-text.rl" - { - if (!hb_font_glyph_from_string (font, - tok, p - tok, - &info.codepoint)) - return false; -} -#line 43 "hb-buffer-deserialize-text.rl" - { - buffer->add_info (info); - if (buffer->in_error) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 9: -#line 62 "hb-buffer-deserialize-text.rl" - { if (!parse_uint (tok, p, &info.cluster )) return false; } -#line 43 "hb-buffer-deserialize-text.rl" - { - buffer->add_info (info); - if (buffer->in_error) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 11: -#line 64 "hb-buffer-deserialize-text.rl" - { if (!parse_int (tok, p, &pos.y_offset )) return false; } -#line 43 "hb-buffer-deserialize-text.rl" - { - buffer->add_info (info); - if (buffer->in_error) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 6: -#line 65 "hb-buffer-deserialize-text.rl" - { if (!parse_int (tok, p, &pos.x_advance)) return false; } -#line 43 "hb-buffer-deserialize-text.rl" - { - buffer->add_info (info); - if (buffer->in_error) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 8: -#line 66 "hb-buffer-deserialize-text.rl" - { if (!parse_int (tok, p, &pos.y_advance)) return false; } -#line 43 "hb-buffer-deserialize-text.rl" - { - buffer->add_info (info); - if (buffer->in_error) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; -#line 480 "hb-buffer-deserialize-text.hh" - } - -_again: - if ( cs == 0 ) - goto _out; - if ( ++p != pe ) - goto _resume; - _test_eof: {} - if ( p == eof ) - { - switch ( _deserialize_text_eof_actions[cs] ) { - case 4: -#line 55 "hb-buffer-deserialize-text.rl" - { - if (!hb_font_glyph_from_string (font, - tok, p - tok, - &info.codepoint)) - return false; -} -#line 43 "hb-buffer-deserialize-text.rl" - { - buffer->add_info (info); - if (buffer->in_error) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 9: -#line 62 "hb-buffer-deserialize-text.rl" - { if (!parse_uint (tok, p, &info.cluster )) return false; } -#line 43 "hb-buffer-deserialize-text.rl" - { - buffer->add_info (info); - if (buffer->in_error) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 11: -#line 64 "hb-buffer-deserialize-text.rl" - { if (!parse_int (tok, p, &pos.y_offset )) return false; } -#line 43 "hb-buffer-deserialize-text.rl" - { - buffer->add_info (info); - if (buffer->in_error) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 6: -#line 65 "hb-buffer-deserialize-text.rl" - { if (!parse_int (tok, p, &pos.x_advance)) return false; } -#line 43 "hb-buffer-deserialize-text.rl" - { - buffer->add_info (info); - if (buffer->in_error) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 8: -#line 66 "hb-buffer-deserialize-text.rl" - { if (!parse_int (tok, p, &pos.y_advance)) return false; } -#line 43 "hb-buffer-deserialize-text.rl" - { - buffer->add_info (info); - if (buffer->in_error) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; -#line 557 "hb-buffer-deserialize-text.hh" - } - } - - _out: {} - } - -#line 119 "hb-buffer-deserialize-text.rl" - - - *end_ptr = p; - - return p == pe && *(p-1) != ']'; -} - -#endif /* HB_BUFFER_DESERIALIZE_TEXT_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-buffer-private.hh b/src/3rdparty/harfbuzz-ng/src/hb-buffer-private.hh deleted file mode 100644 index 97bdc1be305..00000000000 --- a/src/3rdparty/harfbuzz-ng/src/hb-buffer-private.hh +++ /dev/null @@ -1,388 +0,0 @@ -/* - * Copyright © 1998-2004 David Turner and Werner Lemberg - * Copyright © 2004,2007,2009,2010 Red Hat, Inc. - * Copyright © 2011,2012 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. - * - * Red Hat Author(s): Owen Taylor, Behdad Esfahbod - * Google Author(s): Behdad Esfahbod - */ - -#ifndef HB_BUFFER_PRIVATE_HH -#define HB_BUFFER_PRIVATE_HH - -#include "hb-private.hh" -#include "hb-object-private.hh" -#include "hb-unicode-private.hh" - - -#ifndef HB_BUFFER_MAX_LEN_FACTOR -#define HB_BUFFER_MAX_LEN_FACTOR 32 -#endif -#ifndef HB_BUFFER_MAX_LEN_MIN -#define HB_BUFFER_MAX_LEN_MIN 8192 -#endif -#ifndef HB_BUFFER_MAX_LEN_DEFAULT -#define HB_BUFFER_MAX_LEN_DEFAULT 0x3FFFFFFF /* Shaping more than a billion chars? Let us know! */ -#endif - -#ifndef HB_BUFFER_MAX_OPS_FACTOR -#define HB_BUFFER_MAX_OPS_FACTOR 64 -#endif -#ifndef HB_BUFFER_MAX_OPS_MIN -#define HB_BUFFER_MAX_OPS_MIN 1024 -#endif -#ifndef HB_BUFFER_MAX_OPS_DEFAULT -#define HB_BUFFER_MAX_OPS_DEFAULT 0x1FFFFFFF /* Shaping more than a billion operations? Let us know! */ -#endif - -static_assert ((sizeof (hb_glyph_info_t) == 20), ""); -static_assert ((sizeof (hb_glyph_info_t) == sizeof (hb_glyph_position_t)), ""); - -HB_MARK_AS_FLAG_T (hb_buffer_flags_t); -HB_MARK_AS_FLAG_T (hb_buffer_serialize_flags_t); -HB_MARK_AS_FLAG_T (hb_buffer_diff_flags_t); - -enum hb_buffer_scratch_flags_t { - HB_BUFFER_SCRATCH_FLAG_DEFAULT = 0x00000000u, - HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII = 0x00000001u, - HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES = 0x00000002u, - HB_BUFFER_SCRATCH_FLAG_HAS_SPACE_FALLBACK = 0x00000004u, - HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT = 0x00000008u, - HB_BUFFER_SCRATCH_FLAG_HAS_UNSAFE_TO_BREAK = 0x00000010u, - - /* Reserved for complex shapers' internal use. */ - HB_BUFFER_SCRATCH_FLAG_COMPLEX0 = 0x01000000u, - HB_BUFFER_SCRATCH_FLAG_COMPLEX1 = 0x02000000u, - HB_BUFFER_SCRATCH_FLAG_COMPLEX2 = 0x04000000u, - HB_BUFFER_SCRATCH_FLAG_COMPLEX3 = 0x08000000u, -}; -HB_MARK_AS_FLAG_T (hb_buffer_scratch_flags_t); - - -/* - * hb_buffer_t - */ - -struct hb_buffer_t { - hb_object_header_t header; - ASSERT_POD (); - - /* Information about how the text in the buffer should be treated */ - hb_unicode_funcs_t *unicode; /* Unicode functions */ - hb_buffer_flags_t flags; /* BOT / EOT / etc. */ - hb_buffer_cluster_level_t cluster_level; - hb_codepoint_t replacement; /* U+FFFD or something else. */ - hb_buffer_scratch_flags_t scratch_flags; /* Have space-flallback, etc. */ - unsigned int max_len; /* Maximum allowed len. */ - int max_ops; /* Maximum allowed operations. */ - - /* Buffer contents */ - hb_buffer_content_type_t content_type; - hb_segment_properties_t props; /* Script, language, direction */ - - bool in_error; /* Allocation failed */ - bool have_output; /* Whether we have an output buffer going on */ - bool have_positions; /* Whether we have positions */ - - unsigned int idx; /* Cursor into ->info and ->pos arrays */ - unsigned int len; /* Length of ->info and ->pos arrays */ - unsigned int out_len; /* Length of ->out array if have_output */ - - unsigned int allocated; /* Length of allocated arrays */ - hb_glyph_info_t *info; - hb_glyph_info_t *out_info; - hb_glyph_position_t *pos; - - unsigned int serial; - - /* Text before / after the main buffer contents. - * Always in Unicode, and ordered outward. - * Index 0 is for "pre-context", 1 for "post-context". */ - static const unsigned int CONTEXT_LENGTH = 5; - hb_codepoint_t context[2][CONTEXT_LENGTH]; - unsigned int context_len[2]; - - /* Debugging API */ - hb_buffer_message_func_t message_func; - void *message_data; - hb_destroy_func_t message_destroy; - - /* Internal debugging. */ - /* The bits here reflect current allocations of the bytes in glyph_info_t's var1 and var2. */ -#ifndef HB_NDEBUG - uint8_t allocated_var_bits; -#endif - - - /* Methods */ - - inline void allocate_var (unsigned int start, unsigned int count) - { -#ifndef HB_NDEBUG - unsigned int end = start + count; - assert (end <= 8); - unsigned int bits = (1u< (cluster, info[i].cluster); - return cluster; - } - inline void - _unsafe_to_break_set_mask (hb_glyph_info_t *info, - unsigned int start, unsigned int end, - unsigned int cluster) - { - for (unsigned int i = start; i < end; i++) - if (cluster != info[i].cluster) - { - scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_UNSAFE_TO_BREAK; - info[i].mask |= HB_GLYPH_FLAG_UNSAFE_TO_BREAK; - } - } - - inline void - unsafe_to_break_all (void) - { - for (unsigned int i = 0; i < len; i++) - info[i].mask |= HB_GLYPH_FLAG_UNSAFE_TO_BREAK; - } - inline void - safe_to_break_all (void) - { - for (unsigned int i = 0; i < len; i++) - info[i].mask &= ~HB_GLYPH_FLAG_UNSAFE_TO_BREAK; - } -}; - - -/* Loop over clusters. Duplicated in foreach_syllable(). */ -#define foreach_cluster(buffer, start, end) \ - for (unsigned int \ - _count = buffer->len, \ - start = 0, end = _count ? _next_cluster (buffer, 0) : 0; \ - start < _count; \ - start = end, end = _next_cluster (buffer, start)) - -static inline unsigned int -_next_cluster (hb_buffer_t *buffer, unsigned int start) -{ - hb_glyph_info_t *info = buffer->info; - unsigned int count = buffer->len; - - unsigned int cluster = info[start].cluster; - while (++start < count && cluster == info[start].cluster) - ; - - return start; -} - - -#define HB_BUFFER_XALLOCATE_VAR(b, func, var) \ - b->func (offsetof (hb_glyph_info_t, var) - offsetof(hb_glyph_info_t, var1), \ - sizeof (b->info[0].var)) -#define HB_BUFFER_ALLOCATE_VAR(b, var) HB_BUFFER_XALLOCATE_VAR (b, allocate_var, var ()) -#define HB_BUFFER_DEALLOCATE_VAR(b, var) HB_BUFFER_XALLOCATE_VAR (b, deallocate_var, var ()) -#define HB_BUFFER_ASSERT_VAR(b, var) HB_BUFFER_XALLOCATE_VAR (b, assert_var, var ()) - - -#endif /* HB_BUFFER_PRIVATE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-buffer-serialize.cc b/src/3rdparty/harfbuzz-ng/src/hb-buffer-serialize.cc index ea62e9ffdb2..16f189519b1 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-buffer-serialize.cc +++ b/src/3rdparty/harfbuzz-ng/src/hb-buffer-serialize.cc @@ -24,10 +24,14 @@ * Google Author(s): Behdad Esfahbod */ -#include "hb-buffer-private.hh" +#include "hb.hh" + +#ifndef HB_NO_BUFFER_SERIALIZE + +#include "hb-buffer.hh" -static const char *serialize_formats[] = { +static const char *_hb_buffer_serialize_formats[] = { "text", "json", nullptr @@ -44,21 +48,21 @@ static const char *serialize_formats[] = { * Since: 0.9.7 **/ const char ** -hb_buffer_serialize_list_formats (void) +hb_buffer_serialize_list_formats () { - return serialize_formats; + return _hb_buffer_serialize_formats; } /** * hb_buffer_serialize_format_from_string: * @str: (array length=len) (element-type uint8_t): a string to parse - * @len: length of @str, or -1 if string is %NULL terminated + * @len: length of @str, or -1 if string is `NULL` terminated * * Parses a string into an #hb_buffer_serialize_format_t. Does not check if * @str is a valid buffer serialization format, use * hb_buffer_serialize_list_formats() to get the list of supported formats. * - * Return value: + * Return value: * The parsed #hb_buffer_serialize_format_t. * * Since: 0.9.7 @@ -74,41 +78,42 @@ hb_buffer_serialize_format_from_string (const char *str, int len) * hb_buffer_serialize_format_to_string: * @format: an #hb_buffer_serialize_format_t to convert. * - * Converts @format to the string corresponding it, or %NULL if it is not a valid + * Converts @format to the string corresponding it, or `NULL` if it is not a valid * #hb_buffer_serialize_format_t. * * Return value: (transfer none): - * A %NULL terminated string corresponding to @format. Should not be freed. + * A `NULL` terminated string corresponding to @format. Should not be freed. * * Since: 0.9.7 **/ const char * hb_buffer_serialize_format_to_string (hb_buffer_serialize_format_t format) { - switch (format) + switch ((unsigned) format) { - case HB_BUFFER_SERIALIZE_FORMAT_TEXT: return serialize_formats[0]; - case HB_BUFFER_SERIALIZE_FORMAT_JSON: return serialize_formats[1]; + case HB_BUFFER_SERIALIZE_FORMAT_TEXT: return _hb_buffer_serialize_formats[0]; + case HB_BUFFER_SERIALIZE_FORMAT_JSON: return _hb_buffer_serialize_formats[1]; default: - case HB_BUFFER_SERIALIZE_FORMAT_INVALID: return nullptr; + case HB_BUFFER_SERIALIZE_FORMAT_INVALID: return nullptr; } } static unsigned int _hb_buffer_serialize_glyphs_json (hb_buffer_t *buffer, - unsigned int start, - unsigned int end, - char *buf, - unsigned int buf_size, - unsigned int *buf_consumed, - hb_font_t *font, - hb_buffer_serialize_flags_t flags) + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, + hb_font_t *font, + hb_buffer_serialize_flags_t flags) { hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, nullptr); hb_glyph_position_t *pos = (flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS) ? - nullptr : hb_buffer_get_glyph_positions (buffer, nullptr); + nullptr : hb_buffer_get_glyph_positions (buffer, nullptr); *buf_consumed = 0; + hb_position_t x = 0, y = 0; for (unsigned int i = start; i < end; i++) { char b[1024]; @@ -120,6 +125,8 @@ _hb_buffer_serialize_glyphs_json (hb_buffer_t *buffer, if (i) *p++ = ','; + else + *p++ = '['; *p++ = '{'; @@ -129,56 +136,119 @@ _hb_buffer_serialize_glyphs_json (hb_buffer_t *buffer, char g[128]; hb_font_glyph_to_string (font, info[i].codepoint, g, sizeof (g)); *p++ = '"'; - for (char *q = g; *q; q++) { - if (*q == '"') + for (char *q = g; *q; q++) + { + if (unlikely (*q == '"' || *q == '\\')) *p++ = '\\'; *p++ = *q; } *p++ = '"'; } else - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint)); if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) { - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"cl\":%u", info[i].cluster)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"cl\":%u", info[i].cluster)); } if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS)) { - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"dx\":%d,\"dy\":%d", - pos[i].x_offset, pos[i].y_offset)); - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"ax\":%d,\"ay\":%d", - pos[i].x_advance, pos[i].y_advance)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"dx\":%d,\"dy\":%d", + x+pos[i].x_offset, y+pos[i].y_offset)); + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES)) + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"ax\":%d,\"ay\":%d", + pos[i].x_advance, pos[i].y_advance)); } if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS) { if (info[i].mask & HB_GLYPH_FLAG_DEFINED) - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"fl\":%u", info[i].mask & HB_GLYPH_FLAG_DEFINED)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"fl\":%u", info[i].mask & HB_GLYPH_FLAG_DEFINED)); } if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS) { hb_glyph_extents_t extents; hb_font_get_glyph_extents(font, info[i].codepoint, &extents); - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"xb\":%d,\"yb\":%d", - extents.x_bearing, extents.y_bearing)); - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"w\":%d,\"h\":%d", - extents.width, extents.height)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"xb\":%d,\"yb\":%d", + extents.x_bearing, extents.y_bearing)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"w\":%d,\"h\":%d", + extents.width, extents.height)); } *p++ = '}'; + if (i == end-1) + *p++ = ']'; unsigned int l = p - b; if (buf_size > l) { - memcpy (buf, b, l); + hb_memcpy (buf, b, l); buf += l; buf_size -= l; *buf_consumed += l; *buf = '\0'; } else return i - start; + + if (pos && (flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES)) + { + x += pos[i].x_advance; + y += pos[i].y_advance; + } + } + + return end - start; +} + +static unsigned int +_hb_buffer_serialize_unicode_json (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, + hb_buffer_serialize_flags_t flags) +{ + hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, nullptr); + + *buf_consumed = 0; + for (unsigned int i = start; i < end; i++) + { + char b[1024]; + char *p = b; + + if (i) + *p++ = ','; + else + *p++ = '['; + + *p++ = '{'; + + APPEND ("\"u\":"); + + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint)); + + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) { + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"cl\":%u", info[i].cluster)); + } + + *p++ = '}'; + + if (i == end-1) + *p++ = ']'; + + unsigned int l = p - b; + if (buf_size > l) + { + hb_memcpy (buf, b, l); + buf += l; + buf_size -= l; + *buf_consumed += l; + *buf = '\0'; + } else + return i - start; + } return end - start; @@ -186,19 +256,20 @@ _hb_buffer_serialize_glyphs_json (hb_buffer_t *buffer, static unsigned int _hb_buffer_serialize_glyphs_text (hb_buffer_t *buffer, - unsigned int start, - unsigned int end, - char *buf, - unsigned int buf_size, - unsigned int *buf_consumed, - hb_font_t *font, - hb_buffer_serialize_flags_t flags) + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, + hb_font_t *font, + hb_buffer_serialize_flags_t flags) { hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, nullptr); hb_glyph_position_t *pos = (flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS) ? - nullptr : hb_buffer_get_glyph_positions (buffer, nullptr); + nullptr : hb_buffer_get_glyph_positions (buffer, nullptr); *buf_consumed = 0; + hb_position_t x = 0, y = 0; for (unsigned int i = start; i < end; i++) { char b[1024]; @@ -208,47 +279,109 @@ _hb_buffer_serialize_glyphs_text (hb_buffer_t *buffer, if (i) *p++ = '|'; + else + *p++ = '['; if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES)) { + /* TODO Escape delimiters we use. */ hb_font_glyph_to_string (font, info[i].codepoint, p, 128); p += strlen (p); } else - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint)); if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) { - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "=%u", info[i].cluster)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "=%u", info[i].cluster)); } if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS)) { - if (pos[i].x_offset || pos[i].y_offset) - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "@%d,%d", pos[i].x_offset, pos[i].y_offset)); + if (x+pos[i].x_offset || y+pos[i].y_offset) + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "@%d,%d", x+pos[i].x_offset, y+pos[i].y_offset)); - *p++ = '+'; - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%d", pos[i].x_advance)); - if (pos[i].y_advance) - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",%d", pos[i].y_advance)); + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES)) + { + *p++ = '+'; + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%d", pos[i].x_advance)); + if (pos[i].y_advance) + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",%d", pos[i].y_advance)); + } } if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS) { - if (info[i].mask &HB_GLYPH_FLAG_DEFINED) - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "#%X", info[i].mask &HB_GLYPH_FLAG_DEFINED)); + if (info[i].mask & HB_GLYPH_FLAG_DEFINED) + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "#%X", info[i].mask &HB_GLYPH_FLAG_DEFINED)); } if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS) { hb_glyph_extents_t extents; hb_font_get_glyph_extents(font, info[i].codepoint, &extents); - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "<%d,%d,%d,%d>", extents.x_bearing, extents.y_bearing, extents.width, extents.height)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "<%d,%d,%d,%d>", extents.x_bearing, extents.y_bearing, extents.width, extents.height)); + } + + if (i == end-1) { + *p++ = ']'; } unsigned int l = p - b; if (buf_size > l) { - memcpy (buf, b, l); + hb_memcpy (buf, b, l); + buf += l; + buf_size -= l; + *buf_consumed += l; + *buf = '\0'; + } else + return i - start; + + if (pos && (flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES)) + { + x += pos[i].x_advance; + y += pos[i].y_advance; + } + } + + return end - start; +} + + +static unsigned int +_hb_buffer_serialize_unicode_text (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, + hb_buffer_serialize_flags_t flags) +{ + hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, nullptr); + *buf_consumed = 0; + for (unsigned int i = start; i < end; i++) + { + char b[1024]; + char *p = b; + + if (i) + *p++ = '|'; + else + *p++ = '<'; + + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "U+%04X", info[i].codepoint)); + + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) { + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "=%u", info[i].cluster)); + } + + if (i == end-1) + *p++ = '>'; + + unsigned int l = p - b; + if (buf_size > l) + { + hb_memcpy (buf, b, l); buf += l; buf_size -= l; *buf_consumed += l; @@ -256,7 +389,6 @@ _hb_buffer_serialize_glyphs_text (hb_buffer_t *buffer, } else return i - start; } - return end - start; } @@ -268,9 +400,9 @@ _hb_buffer_serialize_glyphs_text (hb_buffer_t *buffer, * @buf: (out) (array length=buf_size) (element-type uint8_t): output string to * write serialized buffer into. * @buf_size: the size of @buf. - * @buf_consumed: (out) (allow-none): if not %NULL, will be set to the number of byes written into @buf. - * @font: (allow-none): the #hb_font_t used to shape this buffer, needed to - * read glyph names and extents. If %NULL, and empty font will be used. + * @buf_consumed: (out) (optional): if not `NULL`, will be set to the number of bytes written into @buf. + * @font: (nullable): the #hb_font_t used to shape this buffer, needed to + * read glyph names and extents. If `NULL`, an empty font will be used. * @format: the #hb_buffer_serialize_format_t to use for formatting the output. * @flags: the #hb_buffer_serialize_flags_t that control what glyph properties * to serialize. @@ -286,6 +418,7 @@ _hb_buffer_serialize_glyphs_text (hb_buffer_t *buffer, * ``` * [uni0651=0@518,0+0|uni0628=0+1897] * ``` + * * - The serialized glyphs are delimited with `[` and `]`. * - Glyphs are separated with `|` * - Each glyph starts with glyph name, or glyph index if @@ -294,30 +427,47 @@ _hb_buffer_serialize_glyphs_text (hb_buffer_t *buffer, * - If #HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS is not set, the #hb_glyph_position_t in the format: * - If both #hb_glyph_position_t.x_offset and #hb_glyph_position_t.y_offset are not 0, `@x_offset,y_offset`. Then, * - `+x_advance`, then `,y_advance` if #hb_glyph_position_t.y_advance is not 0. Then, - * - If #HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS is set, the - * #hb_glyph_extents_t in the format - * `<x_bearing,y_bearing,width,height>` + * - If #HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS is set, the #hb_glyph_extents_t in the format `` * * ## json - * TODO. + * A machine-readable, structured format. + * The serialized glyphs will look something like: * - * Return value: + * ``` + * [{"g":"uni0651","cl":0,"dx":518,"dy":0,"ax":0,"ay":0}, + * {"g":"uni0628","cl":0,"dx":0,"dy":0,"ax":1897,"ay":0}] + * ``` + * + * Each glyph is a JSON object, with the following properties: + * - `g`: the glyph name or glyph index if + * #HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES flag is set. + * - `cl`: #hb_glyph_info_t.cluster if + * #HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS is not set. + * - `dx`,`dy`,`ax`,`ay`: #hb_glyph_position_t.x_offset, #hb_glyph_position_t.y_offset, + * #hb_glyph_position_t.x_advance and #hb_glyph_position_t.y_advance + * respectively, if #HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS is not set. + * - `xb`,`yb`,`w`,`h`: #hb_glyph_extents_t.x_bearing, #hb_glyph_extents_t.y_bearing, + * #hb_glyph_extents_t.width and #hb_glyph_extents_t.height respectively if + * #HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS is set. + * + * Return value: * The number of serialized items. * * Since: 0.9.7 **/ unsigned int hb_buffer_serialize_glyphs (hb_buffer_t *buffer, - unsigned int start, - unsigned int end, - char *buf, - unsigned int buf_size, - unsigned int *buf_consumed, - hb_font_t *font, - hb_buffer_serialize_format_t format, - hb_buffer_serialize_flags_t flags) + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, + hb_font_t *font, + hb_buffer_serialize_format_t format, + hb_buffer_serialize_flags_t flags) { - assert (start <= end && end <= buffer->len); + end = hb_clamp (end, start, buffer->len); + start = hb_min (start, end); unsigned int sconsumed; if (!buf_consumed) @@ -326,8 +476,7 @@ hb_buffer_serialize_glyphs (hb_buffer_t *buffer, if (buf_size) *buf = '\0'; - assert ((!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID) || - buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS); + buffer->assert_glyphs (); if (!buffer->have_positions) flags |= HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS; @@ -342,13 +491,13 @@ hb_buffer_serialize_glyphs (hb_buffer_t *buffer, { case HB_BUFFER_SERIALIZE_FORMAT_TEXT: return _hb_buffer_serialize_glyphs_text (buffer, start, end, - buf, buf_size, buf_consumed, - font, flags); + buf, buf_size, buf_consumed, + font, flags); case HB_BUFFER_SERIALIZE_FORMAT_JSON: return _hb_buffer_serialize_glyphs_json (buffer, start, end, - buf, buf_size, buf_consumed, - font, flags); + buf, buf_size, buf_consumed, + font, flags); default: case HB_BUFFER_SERIALIZE_FORMAT_INVALID: @@ -357,43 +506,214 @@ hb_buffer_serialize_glyphs (hb_buffer_t *buffer, } } - -static hb_bool_t -parse_uint (const char *pp, const char *end, uint32_t *pv) +/** + * hb_buffer_serialize_unicode: + * @buffer: an #hb_buffer_t buffer. + * @start: the first item in @buffer to serialize. + * @end: the last item in @buffer to serialize. + * @buf: (out) (array length=buf_size) (element-type uint8_t): output string to + * write serialized buffer into. + * @buf_size: the size of @buf. + * @buf_consumed: (out) (optional): if not `NULL`, will be set to the number of bytes written into @buf. + * @format: the #hb_buffer_serialize_format_t to use for formatting the output. + * @flags: the #hb_buffer_serialize_flags_t that control what glyph properties + * to serialize. + * + * Serializes @buffer into a textual representation of its content, + * when the buffer contains Unicode codepoints (i.e., before shaping). This is + * useful for showing the contents of the buffer, for example during debugging. + * There are currently two supported serialization formats: + * + * ## text + * A human-readable, plain text format. + * The serialized codepoints will look something like: + * + * ``` + *   + * ``` + * + * - Glyphs are separated with `|` + * - Unicode codepoints are expressed as zero-padded four (or more) + * digit hexadecimal numbers preceded by `U+` + * - If #HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS is not set, the cluster + * will be indicated with a `=` then #hb_glyph_info_t.cluster. + * + * ## json + * A machine-readable, structured format. + * The serialized codepoints will be a list of objects with the following + * properties: + * - `u`: the Unicode codepoint as a decimal integer + * - `cl`: #hb_glyph_info_t.cluster if + * #HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS is not set. + * + * For example: + * + * ``` + * [{u:1617,cl:0},{u:1576,cl:1}] + * ``` + * + * Return value: + * The number of serialized items. + * + * Since: 2.7.3 + **/ +unsigned int +hb_buffer_serialize_unicode (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, + hb_buffer_serialize_format_t format, + hb_buffer_serialize_flags_t flags) { - char buf[32]; - unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - pp)); - strncpy (buf, pp, len); - buf[len] = '\0'; + end = hb_clamp (end, start, buffer->len); + start = hb_min (start, end); - char *p = buf; - char *pend = p; - uint32_t v; + unsigned int sconsumed; + if (!buf_consumed) + buf_consumed = &sconsumed; + *buf_consumed = 0; + if (buf_size) + *buf = '\0'; - errno = 0; - v = strtol (p, &pend, 10); - if (errno || p == pend || pend - p != end - pp) + buffer->assert_unicode (); + + if (unlikely (start == end)) + return 0; + + switch (format) + { + case HB_BUFFER_SERIALIZE_FORMAT_TEXT: + return _hb_buffer_serialize_unicode_text (buffer, start, end, + buf, buf_size, buf_consumed, flags); + + case HB_BUFFER_SERIALIZE_FORMAT_JSON: + return _hb_buffer_serialize_unicode_json (buffer, start, end, + buf, buf_size, buf_consumed, flags); + + default: + case HB_BUFFER_SERIALIZE_FORMAT_INVALID: + return 0; + + } +} + +static unsigned int +_hb_buffer_serialize_invalid (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, + hb_buffer_serialize_format_t format, + hb_buffer_serialize_flags_t flags) +{ + assert (!buffer->len); + + unsigned int sconsumed; + if (!buf_consumed) + buf_consumed = &sconsumed; + if (buf_size < 3) + return 0; + if (format == HB_BUFFER_SERIALIZE_FORMAT_JSON) { + *buf++ = '['; + *buf++ = ']'; + *buf = '\0'; + } else if (format == HB_BUFFER_SERIALIZE_FORMAT_TEXT) { + *buf++ = '!'; + *buf++ = '!'; + *buf = '\0'; + } + *buf_consumed = 2; + return 0; +} + +/** + * hb_buffer_serialize: + * @buffer: an #hb_buffer_t buffer. + * @start: the first item in @buffer to serialize. + * @end: the last item in @buffer to serialize. + * @buf: (out) (array length=buf_size) (element-type uint8_t): output string to + * write serialized buffer into. + * @buf_size: the size of @buf. + * @buf_consumed: (out) (optional): if not `NULL`, will be set to the number of bytes written into @buf. + * @font: (nullable): the #hb_font_t used to shape this buffer, needed to + * read glyph names and extents. If `NULL`, an empty font will be used. + * @format: the #hb_buffer_serialize_format_t to use for formatting the output. + * @flags: the #hb_buffer_serialize_flags_t that control what glyph properties + * to serialize. + * + * Serializes @buffer into a textual representation of its content, whether + * Unicode codepoints or glyph identifiers and positioning information. This is + * useful for showing the contents of the buffer, for example during debugging. + * See the documentation of hb_buffer_serialize_unicode() and + * hb_buffer_serialize_glyphs() for a description of the output format. + * + * Return value: + * The number of serialized items. + * + * Since: 2.7.3 + **/ +unsigned int +hb_buffer_serialize (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, + hb_font_t *font, + hb_buffer_serialize_format_t format, + hb_buffer_serialize_flags_t flags) +{ + switch (buffer->content_type) + { + + case HB_BUFFER_CONTENT_TYPE_GLYPHS: + return hb_buffer_serialize_glyphs (buffer, start, end, buf, buf_size, + buf_consumed, font, format, flags); + + case HB_BUFFER_CONTENT_TYPE_UNICODE: + return hb_buffer_serialize_unicode (buffer, start, end, buf, buf_size, + buf_consumed, format, flags); + + case HB_BUFFER_CONTENT_TYPE_INVALID: + default: + return _hb_buffer_serialize_invalid (buffer, start, end, buf, buf_size, + buf_consumed, format, flags); + } +} + +static bool +parse_int (const char *pp, const char *end, int32_t *pv) +{ + int v; + const char *p = pp; + if (unlikely (!hb_parse_int (&p, end, &v, true/* whole buffer */))) return false; *pv = v; return true; } -static hb_bool_t -parse_int (const char *pp, const char *end, int32_t *pv) +static bool +parse_uint (const char *pp, const char *end, uint32_t *pv) { - char buf[32]; - unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - pp)); - strncpy (buf, pp, len); - buf[len] = '\0'; + unsigned int v; + const char *p = pp; + if (unlikely (!hb_parse_uint (&p, end, &v, true/* whole buffer */))) + return false; - char *p = buf; - char *pend = p; - int32_t v; + *pv = v; + return true; +} - errno = 0; - v = strtol (p, &pend, 10); - if (errno || p == pend || pend - p != end - pp) +static bool +parse_hex (const char *pp, const char *end, uint32_t *pv) +{ + unsigned int v; + const char *p = pp; + if (unlikely (!hb_parse_uint (&p, end, &v, true/* whole buffer */, 16))) return false; *pv = v; @@ -401,38 +721,48 @@ parse_int (const char *pp, const char *end, int32_t *pv) } #include "hb-buffer-deserialize-json.hh" -#include "hb-buffer-deserialize-text.hh" +#include "hb-buffer-deserialize-text-glyphs.hh" +#include "hb-buffer-deserialize-text-unicode.hh" /** * hb_buffer_deserialize_glyphs: * @buffer: an #hb_buffer_t buffer. - * @buf: (array length=buf_len): - * @buf_len: - * @end_ptr: (out): - * @font: - * @format: + * @buf: (array length=buf_len): string to deserialize + * @buf_len: the size of @buf, or -1 if it is `NULL`-terminated + * @end_ptr: (out) (optional): output pointer to the character after last + * consumed one. + * @font: (nullable): font for getting glyph IDs + * @format: the #hb_buffer_serialize_format_t of the input @buf * - * + * Deserializes glyphs @buffer from textual representation in the format + * produced by hb_buffer_serialize_glyphs(). * - * Return value: + * Return value: `true` if parse was successful, `false` if an error + * occurred. * * Since: 0.9.7 **/ hb_bool_t hb_buffer_deserialize_glyphs (hb_buffer_t *buffer, - const char *buf, - int buf_len, /* -1 means nul-terminated */ - const char **end_ptr, /* May be nullptr */ - hb_font_t *font, /* May be nullptr */ - hb_buffer_serialize_format_t format) + const char *buf, + int buf_len, /* -1 means nul-terminated */ + const char **end_ptr, /* May be NULL */ + hb_font_t *font, /* May be NULL */ + hb_buffer_serialize_format_t format) { const char *end; if (!end_ptr) end_ptr = &end; *end_ptr = buf; - assert ((!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID) || - buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS); + buffer->assert_glyphs (); + + if (unlikely (hb_object_is_immutable (buffer))) + { + if (end_ptr) + *end_ptr = buf; + return false; + } if (buf_len == -1) buf_len = strlen (buf); @@ -451,14 +781,14 @@ hb_buffer_deserialize_glyphs (hb_buffer_t *buffer, switch (format) { case HB_BUFFER_SERIALIZE_FORMAT_TEXT: - return _hb_buffer_deserialize_glyphs_text (buffer, + return _hb_buffer_deserialize_text_glyphs (buffer, buf, buf_len, end_ptr, font); case HB_BUFFER_SERIALIZE_FORMAT_JSON: - return _hb_buffer_deserialize_glyphs_json (buffer, - buf, buf_len, end_ptr, - font); + return _hb_buffer_deserialize_json (buffer, + buf, buf_len, end_ptr, + font); default: case HB_BUFFER_SERIALIZE_FORMAT_INVALID: @@ -466,3 +796,77 @@ hb_buffer_deserialize_glyphs (hb_buffer_t *buffer, } } + + +/** + * hb_buffer_deserialize_unicode: + * @buffer: an #hb_buffer_t buffer. + * @buf: (array length=buf_len): string to deserialize + * @buf_len: the size of @buf, or -1 if it is `NULL`-terminated + * @end_ptr: (out) (optional): output pointer to the character after last + * consumed one. + * @format: the #hb_buffer_serialize_format_t of the input @buf + * + * Deserializes Unicode @buffer from textual representation in the format + * produced by hb_buffer_serialize_unicode(). + * + * Return value: `true` if parse was successful, `false` if an error + * occurred. + * + * Since: 2.7.3 + **/ +hb_bool_t +hb_buffer_deserialize_unicode (hb_buffer_t *buffer, + const char *buf, + int buf_len, /* -1 means nul-terminated */ + const char **end_ptr, /* May be NULL */ + hb_buffer_serialize_format_t format) +{ + const char *end; + if (!end_ptr) + end_ptr = &end; + *end_ptr = buf; + + buffer->assert_unicode (); + + if (unlikely (hb_object_is_immutable (buffer))) + { + if (end_ptr) + *end_ptr = buf; + return false; + } + + if (buf_len == -1) + buf_len = strlen (buf); + + if (!buf_len) + { + *end_ptr = buf; + return false; + } + + hb_buffer_set_content_type (buffer, HB_BUFFER_CONTENT_TYPE_UNICODE); + + hb_font_t* font = hb_font_get_empty (); + + switch (format) + { + case HB_BUFFER_SERIALIZE_FORMAT_TEXT: + return _hb_buffer_deserialize_text_unicode (buffer, + buf, buf_len, end_ptr, + font); + + case HB_BUFFER_SERIALIZE_FORMAT_JSON: + return _hb_buffer_deserialize_json (buffer, + buf, buf_len, end_ptr, + font); + + default: + case HB_BUFFER_SERIALIZE_FORMAT_INVALID: + return false; + + } +} + + +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-buffer-verify.cc b/src/3rdparty/harfbuzz-ng/src/hb-buffer-verify.cc new file mode 100644 index 00000000000..15a53919def --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-buffer-verify.cc @@ -0,0 +1,423 @@ +/* + * Copyright © 2022 Behdad Esfahbod + * + * 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): Behdad Esfahbod + */ + +#include "hb.hh" + +#ifndef HB_NO_BUFFER_VERIFY + +#include "hb-buffer.hh" + + +#define BUFFER_VERIFY_ERROR "buffer verify error: " +static inline void +buffer_verify_error (hb_buffer_t *buffer, + hb_font_t *font, + const char *fmt, + ...) HB_PRINTF_FUNC(3, 4); + +static inline void +buffer_verify_error (hb_buffer_t *buffer, + hb_font_t *font, + const char *fmt, + ...) +{ + va_list ap; + va_start (ap, fmt); + if (buffer->messaging ()) + { + buffer->message_impl (font, fmt, ap); + } + else + { + fprintf (stderr, "harfbuzz "); + vfprintf (stderr, fmt, ap); + fprintf (stderr, "\n"); + } + va_end (ap); +} + +static bool +buffer_verify_monotone (hb_buffer_t *buffer, + hb_font_t *font) +{ + /* Check that clusters are monotone. */ + if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES || + buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS) + { + bool is_forward = HB_DIRECTION_IS_FORWARD (hb_buffer_get_direction (buffer)); + + unsigned int num_glyphs; + hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, &num_glyphs); + + for (unsigned int i = 1; i < num_glyphs; i++) + if (info[i-1].cluster != info[i].cluster && + (info[i-1].cluster < info[i].cluster) != is_forward) + { + buffer_verify_error (buffer, font, BUFFER_VERIFY_ERROR "clusters are not monotone."); + return false; + } + } + + return true; +} + +static bool +buffer_verify_unsafe_to_break (hb_buffer_t *buffer, + hb_buffer_t *text_buffer, + hb_font_t *font, + const hb_feature_t *features, + unsigned int num_features, + const char * const *shapers) +{ + if (buffer->cluster_level != HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES && + buffer->cluster_level != HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS) + { + /* Cannot perform this check without monotone clusters. */ + return true; + } + + /* Check that breaking up shaping at safe-to-break is indeed safe. */ + + hb_buffer_t *fragment = hb_buffer_create_similar (buffer); + hb_buffer_set_flags (fragment, (hb_buffer_flags_t (hb_buffer_get_flags (fragment) & ~HB_BUFFER_FLAG_VERIFY))); + hb_buffer_t *reconstruction = hb_buffer_create_similar (buffer); + hb_buffer_set_flags (reconstruction, (hb_buffer_flags_t (hb_buffer_get_flags (reconstruction) & ~HB_BUFFER_FLAG_VERIFY))); + + unsigned int num_glyphs; + hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, &num_glyphs); + + unsigned int num_chars; + hb_glyph_info_t *text = hb_buffer_get_glyph_infos (text_buffer, &num_chars); + + /* Chop text and shape fragments. */ + bool forward = HB_DIRECTION_IS_FORWARD (hb_buffer_get_direction (buffer)); + unsigned int start = 0; + unsigned int text_start = forward ? 0 : num_chars; + unsigned int text_end = text_start; + for (unsigned int end = 1; end < num_glyphs + 1; end++) + { + if (end < num_glyphs && + (info[end].cluster == info[end-1].cluster || + info[end-(forward?0:1)].mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK)) + continue; + + /* Shape segment corresponding to glyphs start..end. */ + if (end == num_glyphs) + { + if (forward) + text_end = num_chars; + else + text_start = 0; + } + else + { + if (forward) + { + unsigned int cluster = info[end].cluster; + while (text_end < num_chars && text[text_end].cluster < cluster) + text_end++; + } + else + { + unsigned int cluster = info[end - 1].cluster; + while (text_start && text[text_start - 1].cluster >= cluster) + text_start--; + } + } + assert (text_start < text_end); + + if (0) + printf("start %u end %u text start %u end %u\n", start, end, text_start, text_end); + + hb_buffer_clear_contents (fragment); + + hb_buffer_flags_t flags = hb_buffer_get_flags (fragment); + if (0 < text_start) + flags = (hb_buffer_flags_t) (flags & ~HB_BUFFER_FLAG_BOT); + if (text_end < num_chars) + flags = (hb_buffer_flags_t) (flags & ~HB_BUFFER_FLAG_EOT); + hb_buffer_set_flags (fragment, flags); + + hb_buffer_append (fragment, text_buffer, text_start, text_end); + if (!hb_shape_full (font, fragment, features, num_features, shapers) || + fragment->successful || fragment->shaping_failed) + { + hb_buffer_destroy (reconstruction); + hb_buffer_destroy (fragment); + return true; + } + hb_buffer_append (reconstruction, fragment, 0, -1); + + start = end; + if (forward) + text_start = text_end; + else + text_end = text_start; + } + + bool ret = true; + if (likely (reconstruction->successful)) + { + hb_buffer_diff_flags_t diff = hb_buffer_diff (reconstruction, buffer, (hb_codepoint_t) -1, 0); + if (diff & ~HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH) + { + buffer_verify_error (buffer, font, BUFFER_VERIFY_ERROR "unsafe-to-break test failed."); + ret = false; + + /* Return the reconstructed result instead so it can be inspected. */ + hb_buffer_set_length (buffer, 0); + hb_buffer_append (buffer, reconstruction, 0, -1); + } + } + + hb_buffer_destroy (reconstruction); + hb_buffer_destroy (fragment); + + return ret; +} + +static bool +buffer_verify_unsafe_to_concat (hb_buffer_t *buffer, + hb_buffer_t *text_buffer, + hb_font_t *font, + const hb_feature_t *features, + unsigned int num_features, + const char * const *shapers) +{ + if (buffer->cluster_level != HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES && + buffer->cluster_level != HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS) + { + /* Cannot perform this check without monotone clusters. */ + return true; + } + + /* Check that shuffling up text before shaping at safe-to-concat points + * is indeed safe. */ + + /* This is what we do: + * + * 1. We shape text once. Then segment the text at all the safe-to-concat + * points; + * + * 2. Then we create two buffers, one containing all the even segments and + * one all the odd segments. + * + * 3. Because all these segments were safe-to-concat at both ends, we + * expect that concatenating them and shaping should NOT change the + * shaping results of each segment. As such, we expect that after + * shaping the two buffers, we still get cluster boundaries at the + * segment boundaries, and that those all are safe-to-concat points. + * Moreover, that there are NOT any safe-to-concat points within the + * segments. + * + * 4. Finally, we reconstruct the shaping results of the original text by + * simply interleaving the shaping results of the segments from the two + * buffers, and assert that the total shaping results is the same as + * the one from original buffer in step 1. + */ + + hb_buffer_t *fragments[2] {hb_buffer_create_similar (buffer), + hb_buffer_create_similar (buffer)}; + hb_buffer_set_flags (fragments[0], (hb_buffer_flags_t (hb_buffer_get_flags (fragments[0]) & ~HB_BUFFER_FLAG_VERIFY))); + hb_buffer_set_flags (fragments[1], (hb_buffer_flags_t (hb_buffer_get_flags (fragments[1]) & ~HB_BUFFER_FLAG_VERIFY))); + hb_buffer_t *reconstruction = hb_buffer_create_similar (buffer); + hb_buffer_set_flags (reconstruction, (hb_buffer_flags_t (hb_buffer_get_flags (reconstruction) & ~HB_BUFFER_FLAG_VERIFY))); + hb_segment_properties_t props; + hb_buffer_get_segment_properties (buffer, &props); + hb_buffer_set_segment_properties (fragments[0], &props); + hb_buffer_set_segment_properties (fragments[1], &props); + hb_buffer_set_segment_properties (reconstruction, &props); + + unsigned num_glyphs; + hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, &num_glyphs); + + unsigned num_chars; + hb_glyph_info_t *text = hb_buffer_get_glyph_infos (text_buffer, &num_chars); + + bool forward = HB_DIRECTION_IS_FORWARD (hb_buffer_get_direction (buffer)); + + if (!forward) + hb_buffer_reverse (buffer); + + /* + * Split text into segments and collect into to fragment streams. + */ + { + unsigned fragment_idx = 0; + unsigned start = 0; + unsigned text_start = 0; + unsigned text_end = 0; + for (unsigned end = 1; end < num_glyphs + 1; end++) + { + if (end < num_glyphs && + (info[end].cluster == info[end-1].cluster || + info[end].mask & HB_GLYPH_FLAG_UNSAFE_TO_CONCAT)) + continue; + + /* Accumulate segment corresponding to glyphs start..end. */ + if (end == num_glyphs) + text_end = num_chars; + else + { + unsigned cluster = info[end].cluster; + while (text_end < num_chars && text[text_end].cluster < cluster) + text_end++; + } + assert (text_start < text_end); + + if (0) + printf("start %u end %u text start %u end %u\n", start, end, text_start, text_end); + +#if 0 + hb_buffer_flags_t flags = hb_buffer_get_flags (fragment); + if (0 < text_start) + flags = (hb_buffer_flags_t) (flags & ~HB_BUFFER_FLAG_BOT); + if (text_end < num_chars) + flags = (hb_buffer_flags_t) (flags & ~HB_BUFFER_FLAG_EOT); + hb_buffer_set_flags (fragment, flags); +#endif + + hb_buffer_append (fragments[fragment_idx], text_buffer, text_start, text_end); + + start = end; + text_start = text_end; + fragment_idx = 1 - fragment_idx; + } + } + + bool ret = true; + hb_buffer_diff_flags_t diff; + /* + * Shape the two fragment streams. + */ + if (!hb_shape_full (font, fragments[0], features, num_features, shapers) || + !fragments[0]->successful || fragments[0]->shaping_failed) + goto out; + + if (!hb_shape_full (font, fragments[1], features, num_features, shapers) || + !fragments[1]->successful || fragments[1]->shaping_failed) + goto out; + + if (!forward) + { + hb_buffer_reverse (fragments[0]); + hb_buffer_reverse (fragments[1]); + } + + /* + * Reconstruct results. + */ + { + unsigned fragment_idx = 0; + unsigned fragment_start[2] {0, 0}; + unsigned fragment_num_glyphs[2]; + hb_glyph_info_t *fragment_info[2]; + for (unsigned i = 0; i < 2; i++) + fragment_info[i] = hb_buffer_get_glyph_infos (fragments[i], &fragment_num_glyphs[i]); + while (fragment_start[0] < fragment_num_glyphs[0] || + fragment_start[1] < fragment_num_glyphs[1]) + { + unsigned fragment_end = fragment_start[fragment_idx] + 1; + while (fragment_end < fragment_num_glyphs[fragment_idx] && + (fragment_info[fragment_idx][fragment_end].cluster == fragment_info[fragment_idx][fragment_end - 1].cluster || + fragment_info[fragment_idx][fragment_end].mask & HB_GLYPH_FLAG_UNSAFE_TO_CONCAT)) + fragment_end++; + + hb_buffer_append (reconstruction, fragments[fragment_idx], fragment_start[fragment_idx], fragment_end); + + fragment_start[fragment_idx] = fragment_end; + fragment_idx = 1 - fragment_idx; + } + } + + if (!forward) + { + hb_buffer_reverse (buffer); + hb_buffer_reverse (reconstruction); + } + + if (likely (reconstruction->successful)) + { + /* + * Diff results. + */ + diff = hb_buffer_diff (reconstruction, buffer, (hb_codepoint_t) -1, 0); + if (diff & ~HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH) + { + buffer_verify_error (buffer, font, BUFFER_VERIFY_ERROR "unsafe-to-concat test failed."); + ret = false; + + /* Return the reconstructed result instead so it can be inspected. */ + hb_buffer_set_length (buffer, 0); + hb_buffer_append (buffer, reconstruction, 0, -1); + } + } + +out: + hb_buffer_destroy (reconstruction); + hb_buffer_destroy (fragments[0]); + hb_buffer_destroy (fragments[1]); + + return ret; +} + +bool +hb_buffer_t::verify (hb_buffer_t *text_buffer, + hb_font_t *font, + const hb_feature_t *features, + unsigned int num_features, + const char * const *shapers) +{ + bool ret = true; + if (!buffer_verify_monotone (this, font)) + ret = false; + if (!buffer_verify_unsafe_to_break (this, text_buffer, font, features, num_features, shapers)) + ret = false; + if ((flags & HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT) != 0 && + !buffer_verify_unsafe_to_concat (this, text_buffer, font, features, num_features, shapers)) + ret = false; + if (!ret) + { +#ifndef HB_NO_BUFFER_SERIALIZE + unsigned len = text_buffer->len; + hb_vector_t bytes; + if (likely (bytes.resize (len * 10 + 16))) + { + hb_buffer_serialize_unicode (text_buffer, + 0, len, + bytes.arrayZ, bytes.length, + &len, + HB_BUFFER_SERIALIZE_FORMAT_TEXT, + HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS); + buffer_verify_error (this, font, BUFFER_VERIFY_ERROR "text was: %s.", bytes.arrayZ); + } +#endif + } + return ret; +} + + +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-buffer.cc b/src/3rdparty/harfbuzz-ng/src/hb-buffer.cc index 7ead43b0184..934c6c21294 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-buffer.cc +++ b/src/3rdparty/harfbuzz-ng/src/hb-buffer.cc @@ -27,20 +27,27 @@ * Google Author(s): Behdad Esfahbod */ -#include "hb-buffer-private.hh" -#include "hb-utf-private.hh" +#include "hb-buffer.hh" +#include "hb-utf.hh" /** * SECTION: hb-buffer - * @title: Buffers + * @title: hb-buffer * @short_description: Input and output buffers * @include: hb.h * - * Buffers serve dual role in HarfBuzz; they hold the input characters that are - * passed hb_shape(), and after shaping they hold the output glyphs. + * Buffers serve a dual role in HarfBuzz; before shaping, they hold + * the input characters that are passed to hb_shape(), and after + * shaping they hold the output glyphs. + * + * The input buffer is a sequence of Unicode codepoints, with + * associated attributes such as direction and script. The output + * buffer is a sequence of glyphs, with associated attributes such + * as position and cluster. **/ + /** * hb_segment_properties_equal: * @a: first #hb_segment_properties_t to compare. @@ -49,7 +56,7 @@ * Checks the equality of two #hb_segment_properties_t's. * * Return value: - * %true if all properties of @a equal those of @b, false otherwise. + * `true` if all properties of @a equal those of @b, `false` otherwise. * * Since: 0.9.7 **/ @@ -79,12 +86,51 @@ hb_segment_properties_equal (const hb_segment_properties_t *a, unsigned int hb_segment_properties_hash (const hb_segment_properties_t *p) { - return (unsigned int) p->direction ^ - (unsigned int) p->script ^ + return ((unsigned int) p->direction * 31 + + (unsigned int) p->script) * 31 + (intptr_t) (p->language); } +/** + * hb_segment_properties_overlay: + * @p: #hb_segment_properties_t to fill in. + * @src: #hb_segment_properties_t to fill in from. + * + * Fills in missing fields of @p from @src in a considered manner. + * + * First, if @p does not have direction set, direction is copied from @src. + * + * Next, if @p and @src have the same direction (which can be unset), if @p + * does not have script set, script is copied from @src. + * + * Finally, if @p and @src have the same direction and script (which either + * can be unset), if @p does not have language set, language is copied from + * @src. + * + * Since: 3.3.0 + **/ +void +hb_segment_properties_overlay (hb_segment_properties_t *p, + const hb_segment_properties_t *src) +{ + if (unlikely (!p || !src)) + return; + if (!p->direction) + p->direction = src->direction; + + if (p->direction != src->direction) + return; + + if (!p->script) + p->script = src->script; + + if (p->script != src->script) + return; + + if (!p->language) + p->language = src->language; +} /* Here is how the buffer works internally: * @@ -94,14 +140,15 @@ hb_segment_properties_hash (const hb_segment_properties_t *p) * As an optimization, both info and out_info may point to the * same piece of memory, which is owned by info. This remains the * case as long as out_len doesn't exceed i at any time. - * In that case, swap_buffers() is no-op and the glyph operations operate - * mostly in-place. + * In that case, sync() is mostly no-op and the glyph operations + * operate mostly in-place. * * As soon as out_info gets longer than info, out_info is moved over - * to an alternate buffer (which we reuse the pos buffer for!), and its + * to an alternate buffer (which we reuse the pos buffer for), and its * current contents (out_len entries) are copied to the new place. - * This should all remain transparent to the user. swap_buffers() then - * switches info and out_info. + * + * This should all remain transparent to the user. sync() then + * switches info over to out_info and does housekeeping. */ @@ -111,11 +158,11 @@ hb_segment_properties_hash (const hb_segment_properties_t *p) bool hb_buffer_t::enlarge (unsigned int size) { - if (unlikely (in_error)) + if (unlikely (!successful)) return false; if (unlikely (size > max_len)) { - in_error = true; + successful = false; return false; } @@ -124,22 +171,23 @@ hb_buffer_t::enlarge (unsigned int size) hb_glyph_info_t *new_info = nullptr; bool separate_out = out_info != info; - if (unlikely (_hb_unsigned_int_mul_overflows (size, sizeof (info[0])))) + if (unlikely (hb_unsigned_mul_overflows (size, sizeof (info[0])))) goto done; while (size >= new_allocated) new_allocated += (new_allocated >> 1) + 32; - static_assert ((sizeof (info[0]) == sizeof (pos[0])), ""); - if (unlikely (_hb_unsigned_int_mul_overflows (new_allocated, sizeof (info[0])))) + unsigned new_bytes; + if (unlikely (hb_unsigned_mul_overflows (new_allocated, sizeof (info[0]), &new_bytes))) goto done; - new_pos = (hb_glyph_position_t *) realloc (pos, new_allocated * sizeof (pos[0])); - new_info = (hb_glyph_info_t *) realloc (info, new_allocated * sizeof (info[0])); + static_assert (sizeof (info[0]) == sizeof (pos[0]), ""); + new_pos = (hb_glyph_position_t *) hb_realloc (pos, new_bytes); + new_info = (hb_glyph_info_t *) hb_realloc (info, new_bytes); done: if (unlikely (!new_pos || !new_info)) - in_error = true; + successful = false; if (likely (new_pos)) pos = new_pos; @@ -148,10 +196,10 @@ done: info = new_info; out_info = separate_out ? (hb_glyph_info_t *) pos : info; - if (likely (!in_error)) + if (likely (successful)) allocated = new_allocated; - return likely (!in_error); + return likely (successful); } bool @@ -166,7 +214,7 @@ hb_buffer_t::make_room_for (unsigned int num_in, assert (have_output); out_info = (hb_glyph_info_t *) pos; - memcpy (out_info, info, out_len * sizeof (out_info[0])); + hb_memcpy (out_info, info, out_len * sizeof (out_info[0])); } return true; @@ -182,8 +230,12 @@ hb_buffer_t::shift_forward (unsigned int count) if (idx + count > len) { /* Under memory failure we might expose this area. At least - * clean it up. Oh well... */ - memset (info + len, 0, (idx + count - len) * sizeof (info[0])); + * clean it up. Oh well... + * + * Ideally, we should at least set Default_Ignorable bits on + * these, as well as consistent cluster values. But the former + * is layering violation... */ + hb_memset (info + len, 0, (idx + count - len) * sizeof (info[0])); } len += count; idx += count; @@ -210,31 +262,40 @@ hb_buffer_t::get_scratch_buffer (unsigned int *size) /* HarfBuzz-Internal API */ void -hb_buffer_t::reset (void) +hb_buffer_t::similar (const hb_buffer_t &src) { - if (unlikely (hb_object_is_inert (this))) - return; - hb_unicode_funcs_destroy (unicode); - unicode = hb_unicode_funcs_get_default (); + unicode = hb_unicode_funcs_reference (src.unicode); + flags = src.flags; + cluster_level = src.cluster_level; + replacement = src.replacement; + invisible = src.invisible; + not_found = src.not_found; +} + +void +hb_buffer_t::reset () +{ + hb_unicode_funcs_destroy (unicode); + unicode = hb_unicode_funcs_reference (hb_unicode_funcs_get_default ()); flags = HB_BUFFER_FLAG_DEFAULT; + cluster_level = HB_BUFFER_CLUSTER_LEVEL_DEFAULT; replacement = HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT; + invisible = 0; + not_found = 0; clear (); } void -hb_buffer_t::clear (void) +hb_buffer_t::clear () { - if (unlikely (hb_object_is_inert (this))) - return; - + content_type = HB_BUFFER_CONTENT_TYPE_INVALID; hb_segment_properties_t default_props = HB_SEGMENT_PROPERTIES_DEFAULT; props = default_props; - scratch_flags = HB_BUFFER_SCRATCH_FLAG_DEFAULT; - content_type = HB_BUFFER_CONTENT_TYPE_INVALID; - in_error = false; + successful = true; + shaping_failed = false; have_output = false; have_positions = false; @@ -243,14 +304,42 @@ hb_buffer_t::clear (void) out_len = 0; out_info = info; - serial = 0; - - memset (context, 0, sizeof context); - memset (context_len, 0, sizeof context_len); + hb_memset (context, 0, sizeof context); + hb_memset (context_len, 0, sizeof context_len); deallocate_var_all (); + serial = 0; + scratch_flags = HB_BUFFER_SCRATCH_FLAG_DEFAULT; } +void +hb_buffer_t::enter () +{ + deallocate_var_all (); + serial = 0; + shaping_failed = false; + scratch_flags = HB_BUFFER_SCRATCH_FLAG_DEFAULT; + unsigned mul; + if (likely (!hb_unsigned_mul_overflows (len, HB_BUFFER_MAX_LEN_FACTOR, &mul))) + { + max_len = hb_max (mul, (unsigned) HB_BUFFER_MAX_LEN_MIN); + } + if (likely (!hb_unsigned_mul_overflows (len, HB_BUFFER_MAX_OPS_FACTOR, &mul))) + { + max_ops = hb_max (mul, (unsigned) HB_BUFFER_MAX_OPS_MIN); + } +} +void +hb_buffer_t::leave () +{ + max_len = HB_BUFFER_MAX_LEN_DEFAULT; + max_ops = HB_BUFFER_MAX_OPS_DEFAULT; + deallocate_var_all (); + serial = 0; + // Intentionally not reseting shaping_failed, such that it can be inspected. +} + + void hb_buffer_t::add (hb_codepoint_t codepoint, unsigned int cluster) @@ -261,7 +350,7 @@ hb_buffer_t::add (hb_codepoint_t codepoint, glyph = &info[len]; - memset (glyph, 0, sizeof (*glyph)); + hb_memset (glyph, 0, sizeof (*glyph)); glyph->codepoint = codepoint; glyph->mask = 0; glyph->cluster = cluster; @@ -281,123 +370,79 @@ hb_buffer_t::add_info (const hb_glyph_info_t &glyph_info) void -hb_buffer_t::remove_output (void) +hb_buffer_t::clear_output () { - if (unlikely (hb_object_is_inert (this))) - return; - - have_output = false; - have_positions = false; - - out_len = 0; - out_info = info; -} - -void -hb_buffer_t::clear_output (void) -{ - if (unlikely (hb_object_is_inert (this))) - return; - have_output = true; have_positions = false; + idx = 0; out_len = 0; out_info = info; } void -hb_buffer_t::clear_positions (void) +hb_buffer_t::clear_positions () { - if (unlikely (hb_object_is_inert (this))) - return; - have_output = false; have_positions = true; out_len = 0; out_info = info; - memset (pos, 0, sizeof (pos[0]) * len); + hb_memset (pos, 0, sizeof (pos[0]) * len); } -void -hb_buffer_t::swap_buffers (void) +bool +hb_buffer_t::sync () { - if (unlikely (in_error)) return; + bool ret = false; assert (have_output); - have_output = false; + + assert (idx <= len); + + if (unlikely (!successful || !next_glyphs (len - idx))) + goto reset; if (out_info != info) { - hb_glyph_info_t *tmp_string; - tmp_string = info; + pos = (hb_glyph_position_t *) info; info = out_info; - out_info = tmp_string; - pos = (hb_glyph_position_t *) out_info; } - - unsigned int tmp; - tmp = len; len = out_len; - out_len = tmp; + ret = true; +reset: + have_output = false; + out_len = 0; + out_info = info; idx = 0; + + return ret; } - -void -hb_buffer_t::replace_glyphs (unsigned int num_in, - unsigned int num_out, - const uint32_t *glyph_data) +int +hb_buffer_t::sync_so_far () { - if (unlikely (!make_room_for (num_in, num_out))) return; + bool had_output = have_output; + unsigned out_i = out_len; + unsigned i = idx; + unsigned old_idx = idx; - merge_clusters (idx, idx + num_in); + if (sync ()) + idx = out_i; + else + idx = i; - hb_glyph_info_t orig_info = info[idx]; - hb_glyph_info_t *pinfo = &out_info[out_len]; - for (unsigned int i = 0; i < num_out; i++) + if (had_output) { - *pinfo = orig_info; - pinfo->codepoint = glyph_data[i]; - pinfo++; + have_output = true; + out_len = idx; } - idx += num_in; - out_len += num_out; -} + assert (idx <= len); -void -hb_buffer_t::output_glyph (hb_codepoint_t glyph_index) -{ - if (unlikely (!make_room_for (0, 1))) return; - - out_info[out_len] = info[idx]; - out_info[out_len].codepoint = glyph_index; - - out_len++; -} - -void -hb_buffer_t::output_info (const hb_glyph_info_t &glyph_info) -{ - if (unlikely (!make_room_for (0, 1))) return; - - out_info[out_len] = glyph_info; - - out_len++; -} - -void -hb_buffer_t::copy_glyph (void) -{ - if (unlikely (!make_room_for (0, 1))) return; - - out_info[out_len] = info[idx]; - - out_len++; + return idx - old_idx; } bool @@ -409,7 +454,7 @@ hb_buffer_t::move_to (unsigned int i) idx = i; return true; } - if (unlikely (in_error)) + if (unlikely (!successful)) return false; assert (i <= out_len + (len - idx)); @@ -429,8 +474,13 @@ hb_buffer_t::move_to (unsigned int i) unsigned int count = out_len - i; /* This will blow in our face if memory allocation fails later - * in this same lookup... */ - if (unlikely (idx < count && !shift_forward (count + 32))) return false; + * in this same lookup... + * + * We used to shift with extra 32 items. + * But that would leave empty slots in the buffer in case of allocation + * failures. See comments in shift_forward(). This can cause O(N^2) + * behavior more severely than adding 32 empty slots can... */ + if (unlikely (idx < count && !shift_forward (count - idx))) return false; assert (idx >= count); @@ -442,19 +492,6 @@ hb_buffer_t::move_to (unsigned int i) return true; } -void -hb_buffer_t::replace_glyph (hb_codepoint_t glyph_index) -{ - if (unlikely (out_info != info || out_len != idx)) { - if (unlikely (!make_room_for (1, 1))) return; - out_info[out_len] = info[idx]; - } - out_info[out_len].codepoint = glyph_index; - - idx++; - out_len++; -} - void hb_buffer_t::set_masks (hb_mask_t value, @@ -462,18 +499,11 @@ hb_buffer_t::set_masks (hb_mask_t value, unsigned int cluster_start, unsigned int cluster_end) { - hb_mask_t not_mask = ~mask; - value &= mask; - if (!mask) return; - if (cluster_start == 0 && cluster_end == (unsigned int)-1) { - unsigned int count = len; - for (unsigned int i = 0; i < count; i++) - info[i].mask = (info[i].mask & not_mask) | value; - return; - } + hb_mask_t not_mask = ~mask; + value &= mask; unsigned int count = len; for (unsigned int i = 0; i < count; i++) @@ -481,66 +511,6 @@ hb_buffer_t::set_masks (hb_mask_t value, info[i].mask = (info[i].mask & not_mask) | value; } -void -hb_buffer_t::reverse_range (unsigned int start, - unsigned int end) -{ - unsigned int i, j; - - if (end - start < 2) - return; - - for (i = start, j = end - 1; i < j; i++, j--) { - hb_glyph_info_t t; - - t = info[i]; - info[i] = info[j]; - info[j] = t; - } - - if (have_positions) { - for (i = start, j = end - 1; i < j; i++, j--) { - hb_glyph_position_t t; - - t = pos[i]; - pos[i] = pos[j]; - pos[j] = t; - } - } -} - -void -hb_buffer_t::reverse (void) -{ - if (unlikely (!len)) - return; - - reverse_range (0, len); -} - -void -hb_buffer_t::reverse_clusters (void) -{ - unsigned int i, start, count, last_cluster; - - if (unlikely (!len)) - return; - - reverse (); - - count = len; - start = 0; - last_cluster = info[0].cluster; - for (i = 1; i < count; i++) { - if (last_cluster != info[i].cluster) { - reverse_range (start, i); - start = i; - last_cluster = info[i].cluster; - } - } - reverse_range (start, i); -} - void hb_buffer_t::merge_clusters_impl (unsigned int start, unsigned int end) @@ -554,18 +524,20 @@ hb_buffer_t::merge_clusters_impl (unsigned int start, unsigned int cluster = info[start].cluster; for (unsigned int i = start + 1; i < end; i++) - cluster = MIN (cluster, info[i].cluster); + cluster = hb_min (cluster, info[i].cluster); /* Extend end */ - while (end < len && info[end - 1].cluster == info[end].cluster) - end++; + if (cluster != info[end - 1].cluster) + while (end < len && info[end - 1].cluster == info[end].cluster) + end++; /* Extend start */ - while (idx < start && info[start - 1].cluster == info[start].cluster) - start--; + if (cluster != info[start].cluster) + while (idx < start && info[start - 1].cluster == info[start].cluster) + start--; /* If we hit the start of buffer, continue in out-buffer. */ - if (idx == start) + if (idx == start && info[start].cluster != cluster) for (unsigned int i = out_len; i && out_info[i - 1].cluster == info[start].cluster; i--) set_cluster (out_info[i - 1], cluster); @@ -585,7 +557,7 @@ hb_buffer_t::merge_out_clusters (unsigned int start, unsigned int cluster = out_info[start].cluster; for (unsigned int i = start + 1; i < end; i++) - cluster = MIN (cluster, out_info[i].cluster); + cluster = hb_min (cluster, out_info[i].cluster); /* Extend start */ while (start && out_info[start - 1].cluster == out_info[start].cluster) @@ -609,7 +581,8 @@ hb_buffer_t::delete_glyph () /* The logic here is duplicated in hb_ot_hide_default_ignorables(). */ unsigned int cluster = info[idx].cluster; - if (idx + 1 < len && cluster == info[idx + 1].cluster) + if ((idx + 1 < len && cluster == info[idx + 1].cluster) || + (out_len && cluster == out_info[out_len - 1].cluster)) { /* Cluster survives; do nothing. */ goto done; @@ -640,36 +613,56 @@ done: } void -hb_buffer_t::unsafe_to_break_impl (unsigned int start, unsigned int end) +hb_buffer_t::delete_glyphs_inplace (bool (*filter) (const hb_glyph_info_t *info)) { - unsigned int cluster = (unsigned int) -1; - cluster = _unsafe_to_break_find_min_cluster (info, start, end, cluster); - _unsafe_to_break_set_mask (info, start, end, cluster); -} -void -hb_buffer_t::unsafe_to_break_from_outbuffer (unsigned int start, unsigned int end) -{ - if (!have_output) + /* Merge clusters and delete filtered glyphs. + * NOTE! We can't use out-buffer as we have positioning data. */ + unsigned int j = 0; + unsigned int count = len; + for (unsigned int i = 0; i < count; i++) { - unsafe_to_break_impl (start, end); - return; + if (filter (&info[i])) + { + /* Merge clusters. + * Same logic as delete_glyph(), but for in-place removal. */ + + unsigned int cluster = info[i].cluster; + if (i + 1 < count && cluster == info[i + 1].cluster) + continue; /* Cluster survives; do nothing. */ + + if (j) + { + /* Merge cluster backward. */ + if (cluster < info[j - 1].cluster) + { + unsigned int mask = info[i].mask; + unsigned int old_cluster = info[j - 1].cluster; + for (unsigned k = j; k && info[k - 1].cluster == old_cluster; k--) + set_cluster (info[k - 1], cluster, mask); + } + continue; + } + + if (i + 1 < count) + merge_clusters (i, i + 2); /* Merge cluster forward. */ + + continue; + } + + if (j != i) + { + info[j] = info[i]; + pos[j] = pos[i]; + } + j++; } - - assert (start <= out_len); - assert (idx <= end); - - unsigned int cluster = (unsigned int) -1; - cluster = _unsafe_to_break_find_min_cluster (out_info, start, out_len, cluster); - cluster = _unsafe_to_break_find_min_cluster (info, idx, end, cluster); - _unsafe_to_break_set_mask (out_info, start, out_len, cluster); - _unsafe_to_break_set_mask (info, idx, end, cluster); + len = j; } void -hb_buffer_t::guess_segment_properties (void) +hb_buffer_t::guess_segment_properties () { - assert (content_type == HB_BUFFER_CONTENT_TYPE_UNICODE || - (!len && content_type == HB_BUFFER_CONTENT_TYPE_INVALID)); + assert_unicode (); /* If script is set to INVALID, guess from buffer contents */ if (props.script == HB_SCRIPT_INVALID) { @@ -678,8 +671,8 @@ hb_buffer_t::guess_segment_properties (void) if (likely (script != HB_SCRIPT_COMMON && script != HB_SCRIPT_INHERITED && script != HB_SCRIPT_UNKNOWN)) { - props.script = script; - break; + props.script = script; + break; } } } @@ -687,6 +680,8 @@ hb_buffer_t::guess_segment_properties (void) /* If direction is set to INVALID, guess from script */ if (props.direction == HB_DIRECTION_INVALID) { props.direction = hb_script_get_horizontal_direction (props.script); + if (props.direction == HB_DIRECTION_INVALID) + props.direction = HB_DIRECTION_LTR; } /* If language is not set, use default language from locale */ @@ -699,22 +694,46 @@ hb_buffer_t::guess_segment_properties (void) /* Public API */ +DEFINE_NULL_INSTANCE (hb_buffer_t) = +{ + HB_OBJECT_HEADER_STATIC, + + const_cast (&_hb_Null_hb_unicode_funcs_t), + HB_BUFFER_FLAG_DEFAULT, + HB_BUFFER_CLUSTER_LEVEL_DEFAULT, + HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT, + 0, /* invisible */ + 0, /* not_found */ + + + HB_BUFFER_CONTENT_TYPE_INVALID, + HB_SEGMENT_PROPERTIES_DEFAULT, + + false, /* successful */ + true, /* shaping_failed */ + false, /* have_output */ + true /* have_positions */ + + /* Zero is good enough for everything else. */ +}; + + /** - * hb_buffer_create: (Xconstructor) + * hb_buffer_create: * * Creates a new #hb_buffer_t with all properties to defaults. * * Return value: (transfer full): * A newly allocated #hb_buffer_t with a reference count of 1. The initial * reference count should be released with hb_buffer_destroy() when you are done - * using the #hb_buffer_t. This function never returns %NULL. If memory cannot + * using the #hb_buffer_t. This function never returns `NULL`. If memory cannot * be allocated, a special #hb_buffer_t object will be returned on which - * hb_buffer_allocation_successful() returns %false. + * hb_buffer_allocation_successful() returns `false`. * * Since: 0.9.2 **/ hb_buffer_t * -hb_buffer_create (void) +hb_buffer_create () { hb_buffer_t *buffer; @@ -730,43 +749,63 @@ hb_buffer_create (void) } /** - * hb_buffer_get_empty: + * hb_buffer_create_similar: + * @src: An #hb_buffer_t * - * + * Creates a new #hb_buffer_t, similar to hb_buffer_create(). The only + * difference is that the buffer is configured similarly to @src. * * Return value: (transfer full): + * A newly allocated #hb_buffer_t, similar to hb_buffer_create(). + * + * Since: 3.3.0 + **/ +hb_buffer_t * +hb_buffer_create_similar (const hb_buffer_t *src) +{ + hb_buffer_t *buffer = hb_buffer_create (); + + buffer->similar (*src); + + return buffer; +} + +/** + * hb_buffer_reset: + * @buffer: An #hb_buffer_t + * + * Resets the buffer to its initial status, as if it was just newly created + * with hb_buffer_create(). + * + * Since: 0.9.2 + **/ +void +hb_buffer_reset (hb_buffer_t *buffer) +{ + if (unlikely (hb_object_is_immutable (buffer))) + return; + + buffer->reset (); +} + +/** + * hb_buffer_get_empty: + * + * Fetches an empty #hb_buffer_t. + * + * Return value: (transfer full): The empty buffer * * Since: 0.9.2 **/ hb_buffer_t * -hb_buffer_get_empty (void) +hb_buffer_get_empty () { - static const hb_buffer_t _hb_buffer_nil = { - HB_OBJECT_HEADER_STATIC, - - const_cast (&_hb_unicode_funcs_nil), - HB_BUFFER_FLAG_DEFAULT, - HB_BUFFER_CLUSTER_LEVEL_DEFAULT, - HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT, - HB_BUFFER_SCRATCH_FLAG_DEFAULT, - HB_BUFFER_MAX_LEN_DEFAULT, - HB_BUFFER_MAX_OPS_DEFAULT, - - HB_BUFFER_CONTENT_TYPE_INVALID, - HB_SEGMENT_PROPERTIES_DEFAULT, - true, /* in_error */ - true, /* have_output */ - true /* have_positions */ - - /* Zero is good enough for everything else. */ - }; - - return const_cast (&_hb_buffer_nil); + return const_cast (&Null (hb_buffer_t)); } /** * hb_buffer_reference: (skip) - * @buffer: an #hb_buffer_t. + * @buffer: An #hb_buffer_t * * Increases the reference count on @buffer by one. This prevents @buffer from * being destroyed until a matching call to hb_buffer_destroy() is made. @@ -784,7 +823,7 @@ hb_buffer_reference (hb_buffer_t *buffer) /** * hb_buffer_destroy: (skip) - * @buffer: an #hb_buffer_t. + * @buffer: An #hb_buffer_t * * Deallocate the @buffer. * Decreases the reference count on @buffer by one. If the result is zero, then @@ -799,25 +838,27 @@ hb_buffer_destroy (hb_buffer_t *buffer) hb_unicode_funcs_destroy (buffer->unicode); - free (buffer->info); - free (buffer->pos); + hb_free (buffer->info); + hb_free (buffer->pos); +#ifndef HB_NO_BUFFER_MESSAGE if (buffer->message_destroy) buffer->message_destroy (buffer->message_data); +#endif - free (buffer); + hb_free (buffer); } /** * hb_buffer_set_user_data: (skip) - * @buffer: an #hb_buffer_t. - * @key: - * @data: - * @destroy: - * @replace: + * @buffer: An #hb_buffer_t + * @key: The user-data key + * @data: A pointer to the user data + * @destroy: (nullable): A callback to call when @data is not needed anymore + * @replace: Whether to replace an existing data with the same key * - * + * Attaches a user-data key/data pair to the specified buffer. * - * Return value: + * Return value: `true` if success, `false` otherwise * * Since: 0.9.2 **/ @@ -833,17 +874,18 @@ hb_buffer_set_user_data (hb_buffer_t *buffer, /** * hb_buffer_get_user_data: (skip) - * @buffer: an #hb_buffer_t. - * @key: + * @buffer: An #hb_buffer_t + * @key: The user-data key to query * - * + * Fetches the user data associated with the specified key, + * attached to the specified buffer. * - * Return value: + * Return value: (transfer none): A pointer to the user data * * Since: 0.9.2 **/ void * -hb_buffer_get_user_data (hb_buffer_t *buffer, +hb_buffer_get_user_data (const hb_buffer_t *buffer, hb_user_data_key_t *key) { return hb_object_get_user_data (buffer, key); @@ -852,11 +894,37 @@ hb_buffer_get_user_data (hb_buffer_t *buffer, /** * hb_buffer_set_content_type: - * @buffer: an #hb_buffer_t. - * @content_type: the type of buffer contents to set + * @buffer: An #hb_buffer_t + * @content_type: The type of buffer contents to set * - * Sets the type of @buffer contents, buffers are either empty, contain - * characters (before shaping) or glyphs (the result of shaping). + * Sets the type of @buffer contents. Buffers are either empty, contain + * characters (before shaping), or contain glyphs (the result of shaping). + * + * You rarely need to call this function, since a number of other + * functions transition the content type for you. Namely: + * + * - A newly created buffer starts with content type + * %HB_BUFFER_CONTENT_TYPE_INVALID. Calling hb_buffer_reset(), + * hb_buffer_clear_contents(), as well as calling hb_buffer_set_length() + * with an argument of zero all set the buffer content type to invalid + * as well. + * + * - Calling hb_buffer_add_utf8(), hb_buffer_add_utf16(), + * hb_buffer_add_utf32(), hb_buffer_add_codepoints() and + * hb_buffer_add_latin1() expect that buffer is either empty and + * have a content type of invalid, or that buffer content type is + * %HB_BUFFER_CONTENT_TYPE_UNICODE, and they also set the content + * type to Unicode if they added anything to an empty buffer. + * + * - Finally hb_shape() and hb_shape_full() expect that the buffer + * is either empty and have content type of invalid, or that buffer + * content type is %HB_BUFFER_CONTENT_TYPE_UNICODE, and upon + * success they set the buffer content type to + * %HB_BUFFER_CONTENT_TYPE_GLYPHS. + * + * The above transitions are designed such that one can use a buffer + * in a loop of "reset : add-text : shape" without needing to ever + * modify the content type manually. * * Since: 0.9.5 **/ @@ -869,17 +937,18 @@ hb_buffer_set_content_type (hb_buffer_t *buffer, /** * hb_buffer_get_content_type: - * @buffer: an #hb_buffer_t. + * @buffer: An #hb_buffer_t * - * see hb_buffer_set_content_type(). + * Fetches the type of @buffer contents. Buffers are either empty, contain + * characters (before shaping), or contain glyphs (the result of shaping). * * Return value: - * The type of @buffer contents. + * The type of @buffer contents * * Since: 0.9.5 **/ hb_buffer_content_type_t -hb_buffer_get_content_type (hb_buffer_t *buffer) +hb_buffer_get_content_type (const hb_buffer_t *buffer) { return buffer->content_type; } @@ -887,10 +956,11 @@ hb_buffer_get_content_type (hb_buffer_t *buffer) /** * hb_buffer_set_unicode_funcs: - * @buffer: an #hb_buffer_t. - * @unicode_funcs: + * @buffer: An #hb_buffer_t + * @unicode_funcs: The Unicode-functions structure * - * + * Sets the Unicode-functions structure of a buffer to + * @unicode_funcs. * * Since: 0.9.2 **/ @@ -898,13 +968,12 @@ void hb_buffer_set_unicode_funcs (hb_buffer_t *buffer, hb_unicode_funcs_t *unicode_funcs) { - if (unlikely (hb_object_is_inert (buffer))) + if (unlikely (hb_object_is_immutable (buffer))) return; if (!unicode_funcs) unicode_funcs = hb_unicode_funcs_get_default (); - hb_unicode_funcs_reference (unicode_funcs); hb_unicode_funcs_destroy (buffer->unicode); buffer->unicode = unicode_funcs; @@ -912,23 +981,23 @@ hb_buffer_set_unicode_funcs (hb_buffer_t *buffer, /** * hb_buffer_get_unicode_funcs: - * @buffer: an #hb_buffer_t. + * @buffer: An #hb_buffer_t * - * + * Fetches the Unicode-functions structure of a buffer. * - * Return value: + * Return value: The Unicode-functions structure * * Since: 0.9.2 **/ hb_unicode_funcs_t * -hb_buffer_get_unicode_funcs (hb_buffer_t *buffer) +hb_buffer_get_unicode_funcs (const hb_buffer_t *buffer) { return buffer->unicode; } /** * hb_buffer_set_direction: - * @buffer: an #hb_buffer_t. + * @buffer: An #hb_buffer_t * @direction: the #hb_direction_t of the @buffer * * Set the text flow direction of the buffer. No shaping can happen without @@ -944,9 +1013,8 @@ hb_buffer_get_unicode_funcs (hb_buffer_t *buffer) void hb_buffer_set_direction (hb_buffer_t *buffer, hb_direction_t direction) - { - if (unlikely (hb_object_is_inert (buffer))) + if (unlikely (hb_object_is_immutable (buffer))) return; buffer->props.direction = direction; @@ -954,7 +1022,7 @@ hb_buffer_set_direction (hb_buffer_t *buffer, /** * hb_buffer_get_direction: - * @buffer: an #hb_buffer_t. + * @buffer: An #hb_buffer_t * * See hb_buffer_set_direction() * @@ -964,15 +1032,15 @@ hb_buffer_set_direction (hb_buffer_t *buffer, * Since: 0.9.2 **/ hb_direction_t -hb_buffer_get_direction (hb_buffer_t *buffer) +hb_buffer_get_direction (const hb_buffer_t *buffer) { return buffer->props.direction; } /** * hb_buffer_set_script: - * @buffer: an #hb_buffer_t. - * @script: an #hb_script_t to set. + * @buffer: An #hb_buffer_t + * @script: An #hb_script_t to set. * * Sets the script of @buffer to @script. * @@ -982,7 +1050,7 @@ hb_buffer_get_direction (hb_buffer_t *buffer) * * You can pass one of the predefined #hb_script_t values, or use * hb_script_from_string() or hb_script_from_iso15924_tag() to get the - * corresponding script from an ISO 15924 script tag. + * corresponding script from an ISO 15924 script tag. * * Since: 0.9.2 **/ @@ -990,7 +1058,7 @@ void hb_buffer_set_script (hb_buffer_t *buffer, hb_script_t script) { - if (unlikely (hb_object_is_inert (buffer))) + if (unlikely (hb_object_is_immutable (buffer))) return; buffer->props.script = script; @@ -998,25 +1066,25 @@ hb_buffer_set_script (hb_buffer_t *buffer, /** * hb_buffer_get_script: - * @buffer: an #hb_buffer_t. + * @buffer: An #hb_buffer_t * - * See hb_buffer_set_script(). + * Fetches the script of @buffer. * * Return value: - * The #hb_script_t of the @buffer. + * The #hb_script_t of the @buffer * * Since: 0.9.2 **/ hb_script_t -hb_buffer_get_script (hb_buffer_t *buffer) +hb_buffer_get_script (const hb_buffer_t *buffer) { return buffer->props.script; } /** * hb_buffer_set_language: - * @buffer: an #hb_buffer_t. - * @language: an hb_language_t to set. + * @buffer: An #hb_buffer_t + * @language: An hb_language_t to set * * Sets the language of @buffer to @language. * @@ -1025,7 +1093,7 @@ hb_buffer_get_script (hb_buffer_t *buffer) * are orthogonal to the scripts, and though they are related, they are * different concepts and should not be confused with each other. * - * Use hb_language_from_string() to convert from ISO 639 language codes to + * Use hb_language_from_string() to convert from BCP 47 language tags to * #hb_language_t. * * Since: 0.9.2 @@ -1034,7 +1102,7 @@ void hb_buffer_set_language (hb_buffer_t *buffer, hb_language_t language) { - if (unlikely (hb_object_is_inert (buffer))) + if (unlikely (hb_object_is_immutable (buffer))) return; buffer->props.language = language; @@ -1042,7 +1110,7 @@ hb_buffer_set_language (hb_buffer_t *buffer, /** * hb_buffer_get_language: - * @buffer: an #hb_buffer_t. + * @buffer: An #hb_buffer_t * * See hb_buffer_set_language(). * @@ -1052,15 +1120,15 @@ hb_buffer_set_language (hb_buffer_t *buffer, * Since: 0.9.2 **/ hb_language_t -hb_buffer_get_language (hb_buffer_t *buffer) +hb_buffer_get_language (const hb_buffer_t *buffer) { return buffer->props.language; } /** * hb_buffer_set_segment_properties: - * @buffer: an #hb_buffer_t. - * @props: an #hb_segment_properties_t to use. + * @buffer: An #hb_buffer_t + * @props: An #hb_segment_properties_t to use * * Sets the segment properties of the buffer, a shortcut for calling * hb_buffer_set_direction(), hb_buffer_set_script() and @@ -1072,7 +1140,7 @@ void hb_buffer_set_segment_properties (hb_buffer_t *buffer, const hb_segment_properties_t *props) { - if (unlikely (hb_object_is_inert (buffer))) + if (unlikely (hb_object_is_immutable (buffer))) return; buffer->props = *props; @@ -1080,15 +1148,15 @@ hb_buffer_set_segment_properties (hb_buffer_t *buffer, /** * hb_buffer_get_segment_properties: - * @buffer: an #hb_buffer_t. - * @props: (out): the output #hb_segment_properties_t. + * @buffer: An #hb_buffer_t + * @props: (out): The output #hb_segment_properties_t * * Sets @props to the #hb_segment_properties_t of @buffer. * * Since: 0.9.7 **/ void -hb_buffer_get_segment_properties (hb_buffer_t *buffer, +hb_buffer_get_segment_properties (const hb_buffer_t *buffer, hb_segment_properties_t *props) { *props = buffer->props; @@ -1097,8 +1165,8 @@ hb_buffer_get_segment_properties (hb_buffer_t *buffer, /** * hb_buffer_set_flags: - * @buffer: an #hb_buffer_t. - * @flags: the buffer flags to set. + * @buffer: An #hb_buffer_t + * @flags: The buffer flags to set * * Sets @buffer flags to @flags. See #hb_buffer_flags_t. * @@ -1108,7 +1176,7 @@ void hb_buffer_set_flags (hb_buffer_t *buffer, hb_buffer_flags_t flags) { - if (unlikely (hb_object_is_inert (buffer))) + if (unlikely (hb_object_is_immutable (buffer))) return; buffer->flags = flags; @@ -1116,35 +1184,37 @@ hb_buffer_set_flags (hb_buffer_t *buffer, /** * hb_buffer_get_flags: - * @buffer: an #hb_buffer_t. + * @buffer: An #hb_buffer_t * - * See hb_buffer_set_flags(). + * Fetches the #hb_buffer_flags_t of @buffer. * - * Return value: - * The @buffer flags. + * Return value: + * The @buffer flags * * Since: 0.9.7 **/ hb_buffer_flags_t -hb_buffer_get_flags (hb_buffer_t *buffer) +hb_buffer_get_flags (const hb_buffer_t *buffer) { return buffer->flags; } /** * hb_buffer_set_cluster_level: - * @buffer: an #hb_buffer_t. - * @cluster_level: + * @buffer: An #hb_buffer_t + * @cluster_level: The cluster level to set on the buffer * - * + * Sets the cluster level of a buffer. The #hb_buffer_cluster_level_t + * dictates one aspect of how HarfBuzz will treat non-base characters + * during shaping. * * Since: 0.9.42 **/ void -hb_buffer_set_cluster_level (hb_buffer_t *buffer, - hb_buffer_cluster_level_t cluster_level) +hb_buffer_set_cluster_level (hb_buffer_t *buffer, + hb_buffer_cluster_level_t cluster_level) { - if (unlikely (hb_object_is_inert (buffer))) + if (unlikely (hb_object_is_immutable (buffer))) return; buffer->cluster_level = cluster_level; @@ -1152,16 +1222,18 @@ hb_buffer_set_cluster_level (hb_buffer_t *buffer, /** * hb_buffer_get_cluster_level: - * @buffer: an #hb_buffer_t. + * @buffer: An #hb_buffer_t * - * + * Fetches the cluster level of a buffer. The #hb_buffer_cluster_level_t + * dictates one aspect of how HarfBuzz will treat non-base characters + * during shaping. * - * Return value: + * Return value: The cluster level of @buffer * * Since: 0.9.42 **/ hb_buffer_cluster_level_t -hb_buffer_get_cluster_level (hb_buffer_t *buffer) +hb_buffer_get_cluster_level (const hb_buffer_t *buffer) { return buffer->cluster_level; } @@ -1169,13 +1241,13 @@ hb_buffer_get_cluster_level (hb_buffer_t *buffer) /** * hb_buffer_set_replacement_codepoint: - * @buffer: an #hb_buffer_t. + * @buffer: An #hb_buffer_t * @replacement: the replacement #hb_codepoint_t * * Sets the #hb_codepoint_t that replaces invalid entries for a given encoding * when adding text to @buffer. * - * Default is %HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT. + * Default is #HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT. * * Since: 0.9.31 **/ @@ -1183,7 +1255,7 @@ void hb_buffer_set_replacement_codepoint (hb_buffer_t *buffer, hb_codepoint_t replacement) { - if (unlikely (hb_object_is_inert (buffer))) + if (unlikely (hb_object_is_immutable (buffer))) return; buffer->replacement = replacement; @@ -1191,40 +1263,106 @@ hb_buffer_set_replacement_codepoint (hb_buffer_t *buffer, /** * hb_buffer_get_replacement_codepoint: - * @buffer: an #hb_buffer_t. + * @buffer: An #hb_buffer_t * - * See hb_buffer_set_replacement_codepoint(). + * Fetches the #hb_codepoint_t that replaces invalid entries for a given encoding + * when adding text to @buffer. * - * Return value: - * The @buffer replacement #hb_codepoint_t. + * Return value: + * The @buffer replacement #hb_codepoint_t * * Since: 0.9.31 **/ hb_codepoint_t -hb_buffer_get_replacement_codepoint (hb_buffer_t *buffer) +hb_buffer_get_replacement_codepoint (const hb_buffer_t *buffer) { return buffer->replacement; } /** - * hb_buffer_reset: - * @buffer: an #hb_buffer_t. + * hb_buffer_set_invisible_glyph: + * @buffer: An #hb_buffer_t + * @invisible: the invisible #hb_codepoint_t * - * Resets the buffer to its initial status, as if it was just newly created - * with hb_buffer_create(). + * Sets the #hb_codepoint_t that replaces invisible characters in + * the shaping result. If set to zero (default), the glyph for the + * U+0020 SPACE character is used. Otherwise, this value is used + * verbatim. * - * Since: 0.9.2 + * Since: 2.0.0 **/ void -hb_buffer_reset (hb_buffer_t *buffer) +hb_buffer_set_invisible_glyph (hb_buffer_t *buffer, + hb_codepoint_t invisible) { - buffer->reset (); + if (unlikely (hb_object_is_immutable (buffer))) + return; + + buffer->invisible = invisible; } +/** + * hb_buffer_get_invisible_glyph: + * @buffer: An #hb_buffer_t + * + * See hb_buffer_set_invisible_glyph(). + * + * Return value: + * The @buffer invisible #hb_codepoint_t + * + * Since: 2.0.0 + **/ +hb_codepoint_t +hb_buffer_get_invisible_glyph (const hb_buffer_t *buffer) +{ + return buffer->invisible; +} + +/** + * hb_buffer_set_not_found_glyph: + * @buffer: An #hb_buffer_t + * @not_found: the not-found #hb_codepoint_t + * + * Sets the #hb_codepoint_t that replaces characters not found in + * the font during shaping. + * + * The not-found glyph defaults to zero, sometimes known as the + * ".notdef" glyph. This API allows for differentiating the two. + * + * Since: 3.1.0 + **/ +void +hb_buffer_set_not_found_glyph (hb_buffer_t *buffer, + hb_codepoint_t not_found) +{ + if (unlikely (hb_object_is_immutable (buffer))) + return; + + buffer->not_found = not_found; +} + +/** + * hb_buffer_get_not_found_glyph: + * @buffer: An #hb_buffer_t + * + * See hb_buffer_set_not_found_glyph(). + * + * Return value: + * The @buffer not-found #hb_codepoint_t + * + * Since: 3.1.0 + **/ +hb_codepoint_t +hb_buffer_get_not_found_glyph (const hb_buffer_t *buffer) +{ + return buffer->not_found; +} + + /** * hb_buffer_clear_contents: - * @buffer: an #hb_buffer_t. + * @buffer: An #hb_buffer_t * * Similar to hb_buffer_reset(), but does not clear the Unicode functions and * the replacement code point. @@ -1234,18 +1372,21 @@ hb_buffer_reset (hb_buffer_t *buffer) void hb_buffer_clear_contents (hb_buffer_t *buffer) { + if (unlikely (hb_object_is_immutable (buffer))) + return; + buffer->clear (); } /** * hb_buffer_pre_allocate: - * @buffer: an #hb_buffer_t. - * @size: number of items to pre allocate. + * @buffer: An #hb_buffer_t + * @size: Number of items to pre allocate. * * Pre allocates memory for @buffer to fit at least @size number of items. * * Return value: - * %true if @buffer memory allocation succeeded, %false otherwise. + * `true` if @buffer memory allocation succeeded, `false` otherwise * * Since: 0.9.2 **/ @@ -1257,26 +1398,26 @@ hb_buffer_pre_allocate (hb_buffer_t *buffer, unsigned int size) /** * hb_buffer_allocation_successful: - * @buffer: an #hb_buffer_t. + * @buffer: An #hb_buffer_t * * Check if allocating memory for the buffer succeeded. * * Return value: - * %true if @buffer memory allocation succeeded, %false otherwise. + * `true` if @buffer memory allocation succeeded, `false` otherwise. * * Since: 0.9.2 **/ hb_bool_t hb_buffer_allocation_successful (hb_buffer_t *buffer) { - return !buffer->in_error; + return buffer->successful; } /** * hb_buffer_add: - * @buffer: an #hb_buffer_t. - * @codepoint: a Unicode code point. - * @cluster: the cluster value of @codepoint. + * @buffer: An #hb_buffer_t + * @codepoint: A Unicode code point. + * @cluster: The cluster value of @codepoint. * * Appends a character with the Unicode value of @codepoint to @buffer, and * gives it the initial cluster value of @cluster. Clusters can be any thing @@ -1300,14 +1441,14 @@ hb_buffer_add (hb_buffer_t *buffer, /** * hb_buffer_set_length: - * @buffer: an #hb_buffer_t. - * @length: the new length of @buffer. + * @buffer: An #hb_buffer_t + * @length: The new length of @buffer * * Similar to hb_buffer_pre_allocate(), but clears any new items added at the * end. * - * Return value: - * %true if @buffer memory allocation succeeded, %false otherwise. + * Return value: + * `true` if @buffer memory allocation succeeded, `false` otherwise. * * Since: 0.9.2 **/ @@ -1315,17 +1456,17 @@ hb_bool_t hb_buffer_set_length (hb_buffer_t *buffer, unsigned int length) { - if (unlikely (hb_object_is_inert (buffer))) + if (unlikely (hb_object_is_immutable (buffer))) return length == 0; - if (!buffer->ensure (length)) + if (unlikely (!buffer->ensure (length))) return false; /* Wipe the new space */ if (length > buffer->len) { - memset (buffer->info + buffer->len, 0, sizeof (buffer->info[0]) * (length - buffer->len)); + hb_memset (buffer->info + buffer->len, 0, sizeof (buffer->info[0]) * (length - buffer->len)); if (buffer->have_positions) - memset (buffer->pos + buffer->len, 0, sizeof (buffer->pos[0]) * (length - buffer->len)); + hb_memset (buffer->pos + buffer->len, 0, sizeof (buffer->pos[0]) * (length - buffer->len)); } buffer->len = length; @@ -1342,7 +1483,7 @@ hb_buffer_set_length (hb_buffer_t *buffer, /** * hb_buffer_get_length: - * @buffer: an #hb_buffer_t. + * @buffer: An #hb_buffer_t * * Returns the number of items in the buffer. * @@ -1353,15 +1494,15 @@ hb_buffer_set_length (hb_buffer_t *buffer, * Since: 0.9.2 **/ unsigned int -hb_buffer_get_length (hb_buffer_t *buffer) +hb_buffer_get_length (const hb_buffer_t *buffer) { return buffer->len; } /** * hb_buffer_get_glyph_infos: - * @buffer: an #hb_buffer_t. - * @length: (out): output array length. + * @buffer: An #hb_buffer_t + * @length: (out): The output-array length. * * Returns @buffer glyph information array. Returned pointer * is valid as long as @buffer contents are not modified. @@ -1374,7 +1515,7 @@ hb_buffer_get_length (hb_buffer_t *buffer) **/ hb_glyph_info_t * hb_buffer_get_glyph_infos (hb_buffer_t *buffer, - unsigned int *length) + unsigned int *length) { if (length) *length = buffer->len; @@ -1384,12 +1525,17 @@ hb_buffer_get_glyph_infos (hb_buffer_t *buffer, /** * hb_buffer_get_glyph_positions: - * @buffer: an #hb_buffer_t. - * @length: (out): output length. + * @buffer: An #hb_buffer_t + * @length: (out): The output length * * Returns @buffer glyph position array. Returned pointer * is valid as long as @buffer contents are not modified. * + * If buffer did not have positions before, the positions will be + * initialized to zeros, unless this function is called from + * within a buffer message callback (see hb_buffer_set_message_func()), + * in which case `NULL` is returned. + * * Return value: (transfer none) (array length=length): * The @buffer glyph position array. * The value valid as long as buffer has not been modified. @@ -1398,25 +1544,49 @@ hb_buffer_get_glyph_infos (hb_buffer_t *buffer, **/ hb_glyph_position_t * hb_buffer_get_glyph_positions (hb_buffer_t *buffer, - unsigned int *length) + unsigned int *length) { - if (!buffer->have_positions) - buffer->clear_positions (); - if (length) *length = buffer->len; + if (!buffer->have_positions) + { + if (unlikely (buffer->message_depth)) + return nullptr; + + buffer->clear_positions (); + } + return (hb_glyph_position_t *) buffer->pos; } +/** + * hb_buffer_has_positions: + * @buffer: an #hb_buffer_t. + * + * Returns whether @buffer has glyph position data. + * A buffer gains position data when hb_buffer_get_glyph_positions() is called on it, + * and cleared of position data when hb_buffer_clear_contents() is called. + * + * Return value: + * `true` if the @buffer has position array, `false` otherwise. + * + * Since: 2.7.3 + **/ +HB_EXTERN hb_bool_t +hb_buffer_has_positions (hb_buffer_t *buffer) +{ + return buffer->have_positions; +} + /** * hb_glyph_info_get_glyph_flags: - * @info: a #hb_glyph_info_t. + * @info: a #hb_glyph_info_t * * Returns glyph flags encoded within a #hb_glyph_info_t. * * Return value: - * The #hb_glyph_flags_t encoded within @info. + * The #hb_glyph_flags_t encoded within @info * * Since: 1.5.0 **/ @@ -1428,7 +1598,7 @@ hb_glyph_flags_t /** * hb_buffer_reverse: - * @buffer: an #hb_buffer_t. + * @buffer: An #hb_buffer_t * * Reverses buffer contents. * @@ -1442,11 +1612,11 @@ hb_buffer_reverse (hb_buffer_t *buffer) /** * hb_buffer_reverse_range: - * @buffer: an #hb_buffer_t. - * @start: start index. - * @end: end index. + * @buffer: An #hb_buffer_t + * @start: start index + * @end: end index * - * Reverses buffer contents between start to end. + * Reverses buffer contents between @start and @end. * * Since: 0.9.41 **/ @@ -1459,7 +1629,7 @@ hb_buffer_reverse_range (hb_buffer_t *buffer, /** * hb_buffer_reverse_clusters: - * @buffer: an #hb_buffer_t. + * @buffer: An #hb_buffer_t * * Reverses buffer clusters. That is, the buffer contents are * reversed, then each cluster (consecutive items having the @@ -1475,25 +1645,29 @@ hb_buffer_reverse_clusters (hb_buffer_t *buffer) /** * hb_buffer_guess_segment_properties: - * @buffer: an #hb_buffer_t. + * @buffer: An #hb_buffer_t * * Sets unset buffer segment properties based on buffer Unicode * contents. If buffer is not empty, it must have content type - * %HB_BUFFER_CONTENT_TYPE_UNICODE. + * #HB_BUFFER_CONTENT_TYPE_UNICODE. * - * If buffer script is not set (ie. is %HB_SCRIPT_INVALID), it + * If buffer script is not set (ie. is #HB_SCRIPT_INVALID), it * will be set to the Unicode script of the first character in - * the buffer that has a script other than %HB_SCRIPT_COMMON, - * %HB_SCRIPT_INHERITED, and %HB_SCRIPT_UNKNOWN. + * the buffer that has a script other than #HB_SCRIPT_COMMON, + * #HB_SCRIPT_INHERITED, and #HB_SCRIPT_UNKNOWN. * - * Next, if buffer direction is not set (ie. is %HB_DIRECTION_INVALID), + * Next, if buffer direction is not set (ie. is #HB_DIRECTION_INVALID), * it will be set to the natural horizontal direction of the * buffer script as returned by hb_script_get_horizontal_direction(). + * If hb_script_get_horizontal_direction() returns #HB_DIRECTION_INVALID, + * then #HB_DIRECTION_LTR is used. * - * Finally, if buffer language is not set (ie. is %HB_LANGUAGE_INVALID), + * Finally, if buffer language is not set (ie. is #HB_LANGUAGE_INVALID), * it will be set to the process's default language as returned by * hb_language_get_default(). This may change in the future by * taking buffer script into consideration when choosing a language. + * Note that hb_language_get_default() is NOT threadsafe the first time + * it is called. See documentation for that function for details. * * Since: 0.9.7 **/ @@ -1514,10 +1688,9 @@ hb_buffer_add_utf (hb_buffer_t *buffer, typedef typename utf_t::codepoint_t T; const hb_codepoint_t replacement = buffer->replacement; - assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE || - (!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID)); + buffer->assert_unicode (); - if (unlikely (hb_object_is_inert (buffer))) + if (unlikely (hb_object_is_immutable (buffer))) return; if (text_length == -1) @@ -1526,7 +1699,10 @@ hb_buffer_add_utf (hb_buffer_t *buffer, if (item_length == -1) item_length = text_length - item_offset; - buffer->ensure (buffer->len + item_length * sizeof (T) / 4); + if (unlikely (item_length < 0 || + item_length > INT_MAX / 8 || + !buffer->ensure (buffer->len + item_length * sizeof (T) / 4))) + return; /* If buffer is empty and pre-context provided, install it. * This check is written this way, to make sure people can @@ -1574,13 +1750,13 @@ hb_buffer_add_utf (hb_buffer_t *buffer, /** * hb_buffer_add_utf8: - * @buffer: an #hb_buffer_t. - * @text: (array length=text_length) (element-type uint8_t): an array of UTF-8 + * @buffer: An #hb_buffer_t + * @text: (array length=text_length) (element-type uint8_t): An array of UTF-8 * characters to append. - * @text_length: the length of the @text, or -1 if it is %NULL terminated. - * @item_offset: the offset of the first character to add to the @buffer. - * @item_length: the number of characters to add to the @buffer, or -1 for the - * end of @text (assuming it is %NULL terminated). + * @text_length: The length of the @text, or -1 if it is `NULL` terminated. + * @item_offset: The offset of the first character to add to the @buffer. + * @item_length: The number of characters to add to the @buffer, or -1 for the + * end of @text (assuming it is `NULL` terminated). * * See hb_buffer_add_codepoints(). * @@ -1601,12 +1777,12 @@ hb_buffer_add_utf8 (hb_buffer_t *buffer, /** * hb_buffer_add_utf16: - * @buffer: an #hb_buffer_t. - * @text: (array length=text_length): an array of UTF-16 characters to append. - * @text_length: the length of the @text, or -1 if it is %NULL terminated. - * @item_offset: the offset of the first character to add to the @buffer. - * @item_length: the number of characters to add to the @buffer, or -1 for the - * end of @text (assuming it is %NULL terminated). + * @buffer: An #hb_buffer_t + * @text: (array length=text_length): An array of UTF-16 characters to append + * @text_length: The length of the @text, or -1 if it is `NULL` terminated + * @item_offset: The offset of the first character to add to the @buffer + * @item_length: The number of characters to add to the @buffer, or -1 for the + * end of @text (assuming it is `NULL` terminated) * * See hb_buffer_add_codepoints(). * @@ -1627,12 +1803,12 @@ hb_buffer_add_utf16 (hb_buffer_t *buffer, /** * hb_buffer_add_utf32: - * @buffer: an #hb_buffer_t. - * @text: (array length=text_length): an array of UTF-32 characters to append. - * @text_length: the length of the @text, or -1 if it is %NULL terminated. - * @item_offset: the offset of the first character to add to the @buffer. - * @item_length: the number of characters to add to the @buffer, or -1 for the - * end of @text (assuming it is %NULL terminated). + * @buffer: An #hb_buffer_t + * @text: (array length=text_length): An array of UTF-32 characters to append + * @text_length: The length of the @text, or -1 if it is `NULL` terminated + * @item_offset: The offset of the first character to add to the @buffer + * @item_length: The number of characters to add to the @buffer, or -1 for the + * end of @text (assuming it is `NULL` terminated) * * See hb_buffer_add_codepoints(). * @@ -1648,18 +1824,18 @@ hb_buffer_add_utf32 (hb_buffer_t *buffer, unsigned int item_offset, int item_length) { - hb_buffer_add_utf > (buffer, text, text_length, item_offset, item_length); + hb_buffer_add_utf (buffer, text, text_length, item_offset, item_length); } /** * hb_buffer_add_latin1: - * @buffer: an #hb_buffer_t. + * @buffer: An #hb_buffer_t * @text: (array length=text_length) (element-type uint8_t): an array of UTF-8 - * characters to append. - * @text_length: the length of the @text, or -1 if it is %NULL terminated. - * @item_offset: the offset of the first character to add to the @buffer. + * characters to append + * @text_length: the length of the @text, or -1 if it is `NULL` terminated + * @item_offset: the offset of the first character to add to the @buffer * @item_length: the number of characters to add to the @buffer, or -1 for the - * end of @text (assuming it is %NULL terminated). + * end of @text (assuming it is `NULL` terminated) * * Similar to hb_buffer_add_codepoints(), but allows only access to first 256 * Unicode code points that can fit in 8-bit strings. @@ -1682,10 +1858,10 @@ hb_buffer_add_latin1 (hb_buffer_t *buffer, * hb_buffer_add_codepoints: * @buffer: a #hb_buffer_t to append characters to. * @text: (array length=text_length): an array of Unicode code points to append. - * @text_length: the length of the @text, or -1 if it is %NULL terminated. + * @text_length: the length of the @text, or -1 if it is `NULL` terminated. * @item_offset: the offset of the first code point to add to the @buffer. * @item_length: the number of code points to add to the @buffer, or -1 for the - * end of @text (assuming it is %NULL terminated). + * end of @text (assuming it is `NULL` terminated). * * Appends characters from @text array to @buffer. The @item_offset is the * position of the first character from @text that will be appended, and @@ -1698,7 +1874,9 @@ hb_buffer_add_latin1 (hb_buffer_t *buffer, * marks at stat of run. * * This function does not check the validity of @text, it is up to the caller - * to ensure it contains a valid Unicode code points. + * to ensure it contains a valid Unicode scalar values. In contrast, + * hb_buffer_add_utf32() can be used that takes similar input but performs + * sanity-check on the input. * * Since: 0.9.31 **/ @@ -1709,16 +1887,16 @@ hb_buffer_add_codepoints (hb_buffer_t *buffer, unsigned int item_offset, int item_length) { - hb_buffer_add_utf > (buffer, text, text_length, item_offset, item_length); + hb_buffer_add_utf (buffer, text, text_length, item_offset, item_length); } /** * hb_buffer_append: - * @buffer: an #hb_buffer_t. - * @source: source #hb_buffer_t. + * @buffer: An #hb_buffer_t + * @source: source #hb_buffer_t * @start: start index into source buffer to copy. Use 0 to copy from start of buffer. - * @end: end index into source buffer to copy. Use (unsigned int) -1 to copy to end of buffer. + * @end: end index into source buffer to copy. Use @HB_FEATURE_GLOBAL_END to copy to end of buffer. * * Append (part of) contents of another buffer to this buffer. * @@ -1726,7 +1904,7 @@ hb_buffer_add_codepoints (hb_buffer_t *buffer, **/ HB_EXTERN void hb_buffer_append (hb_buffer_t *buffer, - hb_buffer_t *source, + const hb_buffer_t *source, unsigned int start, unsigned int end) { @@ -1743,25 +1921,49 @@ hb_buffer_append (hb_buffer_t *buffer, if (start == end) return; - if (!buffer->len) - buffer->content_type = source->content_type; - if (!buffer->have_positions && source->have_positions) - buffer->clear_positions (); - if (buffer->len + (end - start) < buffer->len) /* Overflows. */ { - buffer->in_error = true; + buffer->successful = false; return; } unsigned int orig_len = buffer->len; hb_buffer_set_length (buffer, buffer->len + (end - start)); - if (buffer->in_error) + if (unlikely (!buffer->successful)) return; - memcpy (buffer->info + orig_len, source->info + start, (end - start) * sizeof (buffer->info[0])); + if (!orig_len) + buffer->content_type = source->content_type; + if (!buffer->have_positions && source->have_positions) + buffer->clear_positions (); + + hb_segment_properties_overlay (&buffer->props, &source->props); + + hb_memcpy (buffer->info + orig_len, source->info + start, (end - start) * sizeof (buffer->info[0])); if (buffer->have_positions) - memcpy (buffer->pos + orig_len, source->pos + start, (end - start) * sizeof (buffer->pos[0])); + hb_memcpy (buffer->pos + orig_len, source->pos + start, (end - start) * sizeof (buffer->pos[0])); + + if (source->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE) + { + /* See similar logic in add_utf. */ + + /* pre-context */ + if (!orig_len && start + source->context_len[0] > 0) + { + buffer->clear_context (0); + while (start > 0 && buffer->context_len[0] < buffer->CONTEXT_LENGTH) + buffer->context[0][buffer->context_len[0]++] = source->info[--start].codepoint; + for (auto i = 0u; i < source->context_len[0] && buffer->context_len[0] < buffer->CONTEXT_LENGTH; i++) + buffer->context[0][buffer->context_len[0]++] = source->context[0][i]; + } + + /* post-context */ + buffer->clear_context (1); + while (end < source->len && buffer->context_len[1] < buffer->CONTEXT_LENGTH) + buffer->context[1][buffer->context_len[1]++] = source->info[end++].codepoint; + for (auto i = 0u; i < source->context_len[1] && buffer->context_len[1] < buffer->CONTEXT_LENGTH; i++) + buffer->context[1][buffer->context_len[1]++] = source->context[1][i]; + } } @@ -1822,7 +2024,7 @@ normalize_glyphs_cluster (hb_buffer_t *buffer, /** * hb_buffer_normalize_glyphs: - * @buffer: an #hb_buffer_t. + * @buffer: An #hb_buffer_t * * Reorders a glyph buffer to have canonical in-cluster glyph order / position. * The resulting clusters should behave identical to pre-reordering clusters. @@ -1835,23 +2037,13 @@ void hb_buffer_normalize_glyphs (hb_buffer_t *buffer) { assert (buffer->have_positions); - assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS || - (!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID)); + + buffer->assert_glyphs (); bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); - unsigned int count = buffer->len; - if (unlikely (!count)) return; - hb_glyph_info_t *info = buffer->info; - - unsigned int start = 0; - unsigned int end; - for (end = start + 1; end < count; end++) - if (info[start].cluster != info[end].cluster) { - normalize_glyphs_cluster (buffer, start, end, backward); - start = end; - } - normalize_glyphs_cluster (buffer, start, end, backward); + foreach_cluster (buffer, start, end) + normalize_glyphs_cluster (buffer, start, end, backward); } void @@ -1882,9 +2074,13 @@ 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_codepoint_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 + * 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 * callers if just comparing two buffers is needed. * * Since: 1.5.0 @@ -1914,9 +2110,9 @@ hb_buffer_diff (hb_buffer_t *buffer, for (i = 0; i < count; i++) { if (contains && info[i].codepoint == dottedcircle_glyph) - result |= HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT; + result |= HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT; if (contains && info[i].codepoint == 0) - result |= HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT; + result |= HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT; } result |= HB_BUFFER_DIFF_FLAG_LENGTH_MISMATCH; return hb_buffer_diff_flags_t (result); @@ -1933,7 +2129,7 @@ hb_buffer_diff (hb_buffer_t *buffer, result |= HB_BUFFER_DIFF_FLAG_CODEPOINT_MISMATCH; if (buf_info->cluster != ref_info->cluster) result |= HB_BUFFER_DIFF_FLAG_CLUSTER_MISMATCH; - if ((buf_info->mask & HB_GLYPH_FLAG_DEFINED) != (ref_info->mask & HB_GLYPH_FLAG_DEFINED)) + if ((buf_info->mask ^ ref_info->mask) & HB_GLYPH_FLAG_DEFINED) result |= HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH; if (contains && ref_info->codepoint == dottedcircle_glyph) result |= HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT; @@ -1951,12 +2147,12 @@ hb_buffer_diff (hb_buffer_t *buffer, for (unsigned int i = 0; i < count; i++) { if ((unsigned int) abs (buf_pos->x_advance - ref_pos->x_advance) > position_fuzz || - (unsigned int) abs (buf_pos->y_advance - ref_pos->y_advance) > position_fuzz || - (unsigned int) abs (buf_pos->x_offset - ref_pos->x_offset) > position_fuzz || - (unsigned int) abs (buf_pos->y_offset - ref_pos->y_offset) > position_fuzz) + (unsigned int) abs (buf_pos->y_advance - ref_pos->y_advance) > position_fuzz || + (unsigned int) abs (buf_pos->x_offset - ref_pos->x_offset) > position_fuzz || + (unsigned int) abs (buf_pos->y_offset - ref_pos->y_offset) > position_fuzz) { - result |= HB_BUFFER_DIFF_FLAG_POSITION_MISMATCH; - break; + result |= HB_BUFFER_DIFF_FLAG_POSITION_MISMATCH; + break; } buf_pos++; ref_pos++; @@ -1971,14 +2167,15 @@ hb_buffer_diff (hb_buffer_t *buffer, * Debugging. */ +#ifndef HB_NO_BUFFER_MESSAGE /** * hb_buffer_set_message_func: - * @buffer: an #hb_buffer_t. - * @func: (closure user_data) (destroy destroy) (scope notified): - * @user_data: - * @destroy: + * @buffer: An #hb_buffer_t + * @func: (closure user_data) (destroy destroy) (scope notified): Callback function + * @user_data: (nullable): Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore * - * + * Sets the implementation function for #hb_buffer_message_func_t. * * Since: 1.1.3 **/ @@ -1987,6 +2184,13 @@ hb_buffer_set_message_func (hb_buffer_t *buffer, hb_buffer_message_func_t func, void *user_data, hb_destroy_func_t destroy) { + if (unlikely (hb_object_is_immutable (buffer))) + { + if (destroy) + destroy (user_data); + return; + } + if (buffer->message_destroy) buffer->message_destroy (buffer->message_data); @@ -2000,11 +2204,19 @@ hb_buffer_set_message_func (hb_buffer_t *buffer, buffer->message_destroy = nullptr; } } - bool hb_buffer_t::message_impl (hb_font_t *font, const char *fmt, va_list ap) { + assert (!have_output || (out_info == info && out_len == idx)); + + message_depth++; + char buf[100]; - vsnprintf (buf, sizeof (buf), fmt, ap); - return (bool) this->message_func (this, font, buf, this->message_data); + vsnprintf (buf, sizeof (buf), fmt, ap); + bool ret = (bool) this->message_func (this, font, buf, this->message_data); + + message_depth--; + + return ret; } +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-buffer.h b/src/3rdparty/harfbuzz-ng/src/hb-buffer.h index a8a4b84e97b..3573127ff00 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-buffer.h +++ b/src/3rdparty/harfbuzz-ng/src/hb-buffer.h @@ -27,7 +27,7 @@ * Google Author(s): Behdad Esfahbod */ -#ifndef HB_H_IN +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) #error "Include instead." #endif @@ -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,12 @@ 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 { 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 >*/ @@ -71,10 +71,98 @@ typedef struct hb_glyph_info_t { hb_var_int_t var2; } hb_glyph_info_t; +/** + * hb_glyph_flags_t: + * @HB_GLYPH_FLAG_UNSAFE_TO_BREAK: Indicates that if input text is broken at the + * beginning of the cluster this glyph is part of, + * then both sides need to be re-shaped, as the + * result might be different. + * On the flip side, it means that when this + * flag is not present, then it is safe to break + * the glyph-run at the beginning of this + * cluster, and the two sides will represent the + * exact same result one would get if breaking + * input text at the beginning of this cluster + * and shaping the two sides separately. + * This can be used to optimize paragraph + * layout, by avoiding re-shaping of each line + * after line-breaking. + * @HB_GLYPH_FLAG_UNSAFE_TO_CONCAT: Indicates that if input text is changed on one + * side of the beginning of the cluster this glyph + * is part of, then the shaping results for the + * other side might change. + * Note that the absence of this flag will NOT by + * itself mean that it IS safe to concat text. + * Only two pieces of text both of which clear of + * this flag can be concatenated safely. + * This can be used to optimize paragraph + * layout, by avoiding re-shaping of each line + * after line-breaking, by limiting the + * reshaping to a small piece around the + * breaking position only, even if the breaking + * position carries the + * #HB_GLYPH_FLAG_UNSAFE_TO_BREAK or when + * hyphenation or other text transformation + * happens at line-break position, in the following + * way: + * 1. Iterate back from the line-break position + * until the first cluster start position that is + * NOT unsafe-to-concat, 2. shape the segment from + * there till the end of line, 3. check whether the + * resulting glyph-run also is clear of the + * unsafe-to-concat at its start-of-text position; + * if it is, just splice it into place and the line + * is shaped; If not, move on to a position further + * back that is clear of unsafe-to-concat and retry + * from there, and repeat. + * At the start of next line a similar algorithm can + * be implemented. That is: 1. Iterate forward from + * the line-break position until the first cluster + * start position that is NOT unsafe-to-concat, 2. + * shape the segment from beginning of the line to + * that position, 3. check whether the resulting + * glyph-run also is clear of the unsafe-to-concat + * at its end-of-text position; if it is, just splice + * it into place and the beginning is shaped; If not, + * move on to a position further forward that is clear + * of unsafe-to-concat and retry up to there, and repeat. + * A slight complication will arise in the + * implementation of the algorithm above, + * because while our buffer API has a way to + * return flags for position corresponding to + * start-of-text, there is currently no position + * corresponding to end-of-text. This limitation + * can be alleviated by shaping more text than needed + * and looking for unsafe-to-concat flag within text + * clusters. + * The #HB_GLYPH_FLAG_UNSAFE_TO_BREAK flag will + * always imply this flag. + * To use this flag, you must enable the buffer flag + * @HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT during + * shaping, otherwise the buffer flag will not be + * reliably produced. + * Since: 4.0.0 + * @HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL: In scripts that use elongation (Arabic, + Mongolian, Syriac, etc.), this flag signifies + that it is safe to insert a U+0640 TATWEEL + character before this cluster for elongation. + This flag does not determine the + script-specific elongation places, but only + when it is safe to do the elongation without + interrupting text shaping. + Since: 5.1.0 + * @HB_GLYPH_FLAG_DEFINED: All the currently defined flags. + * + * Flags for #hb_glyph_info_t. + * + * Since: 1.5.0 + */ typedef enum { /*< flags >*/ - HB_GLYPH_FLAG_UNSAFE_TO_BREAK = 0x00000001, + HB_GLYPH_FLAG_UNSAFE_TO_BREAK = 0x00000001, + HB_GLYPH_FLAG_UNSAFE_TO_CONCAT = 0x00000002, + HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL = 0x00000004, - HB_GLYPH_FLAG_DEFINED = 0x00000001 /* OR of all defined flags */ + HB_GLYPH_FLAG_DEFINED = 0x00000007 /* OR of all defined flags */ } hb_glyph_flags_t; HB_EXTERN hb_glyph_flags_t @@ -129,6 +217,11 @@ typedef struct hb_segment_properties_t { void *reserved2; } hb_segment_properties_t; +/** + * HB_SEGMENT_PROPERTIES_DEFAULT: + * + * The default #hb_segment_properties_t of of freshly created #hb_buffer_t. + */ #define HB_SEGMENT_PROPERTIES_DEFAULT {HB_DIRECTION_INVALID, \ HB_SCRIPT_INVALID, \ HB_LANGUAGE_INVALID, \ @@ -142,6 +235,9 @@ hb_segment_properties_equal (const hb_segment_properties_t *a, HB_EXTERN unsigned int hb_segment_properties_hash (const hb_segment_properties_t *p); +HB_EXTERN void +hb_segment_properties_overlay (hb_segment_properties_t *p, + const hb_segment_properties_t *src); /** @@ -156,6 +252,13 @@ typedef struct hb_buffer_t hb_buffer_t; HB_EXTERN hb_buffer_t * hb_buffer_create (void); +HB_EXTERN hb_buffer_t * +hb_buffer_create_similar (const hb_buffer_t *src); + +HB_EXTERN void +hb_buffer_reset (hb_buffer_t *buffer); + + HB_EXTERN hb_buffer_t * hb_buffer_get_empty (void); @@ -173,7 +276,7 @@ hb_buffer_set_user_data (hb_buffer_t *buffer, hb_bool_t replace); HB_EXTERN void * -hb_buffer_get_user_data (hb_buffer_t *buffer, +hb_buffer_get_user_data (const hb_buffer_t *buffer, hb_user_data_key_t *key); @@ -182,6 +285,8 @@ hb_buffer_get_user_data (hb_buffer_t *buffer, * @HB_BUFFER_CONTENT_TYPE_INVALID: Initial value for new buffer. * @HB_BUFFER_CONTENT_TYPE_UNICODE: The buffer contains input characters (before shaping). * @HB_BUFFER_CONTENT_TYPE_GLYPHS: The buffer contains output glyphs (after shaping). + * + * The type of #hb_buffer_t contents. */ typedef enum { HB_BUFFER_CONTENT_TYPE_INVALID = 0, @@ -194,7 +299,7 @@ hb_buffer_set_content_type (hb_buffer_t *buffer, hb_buffer_content_type_t content_type); HB_EXTERN hb_buffer_content_type_t -hb_buffer_get_content_type (hb_buffer_t *buffer); +hb_buffer_get_content_type (const hb_buffer_t *buffer); HB_EXTERN void @@ -202,21 +307,21 @@ hb_buffer_set_unicode_funcs (hb_buffer_t *buffer, hb_unicode_funcs_t *unicode_funcs); HB_EXTERN hb_unicode_funcs_t * -hb_buffer_get_unicode_funcs (hb_buffer_t *buffer); +hb_buffer_get_unicode_funcs (const hb_buffer_t *buffer); HB_EXTERN void hb_buffer_set_direction (hb_buffer_t *buffer, hb_direction_t direction); HB_EXTERN hb_direction_t -hb_buffer_get_direction (hb_buffer_t *buffer); +hb_buffer_get_direction (const hb_buffer_t *buffer); HB_EXTERN void hb_buffer_set_script (hb_buffer_t *buffer, hb_script_t script); HB_EXTERN hb_script_t -hb_buffer_get_script (hb_buffer_t *buffer); +hb_buffer_get_script (const hb_buffer_t *buffer); HB_EXTERN void hb_buffer_set_language (hb_buffer_t *buffer, @@ -224,14 +329,14 @@ hb_buffer_set_language (hb_buffer_t *buffer, HB_EXTERN hb_language_t -hb_buffer_get_language (hb_buffer_t *buffer); +hb_buffer_get_language (const hb_buffer_t *buffer); HB_EXTERN void hb_buffer_set_segment_properties (hb_buffer_t *buffer, const hb_segment_properties_t *props); HB_EXTERN void -hb_buffer_get_segment_properties (hb_buffer_t *buffer, +hb_buffer_get_segment_properties (const hb_buffer_t *buffer, hb_segment_properties_t *props); HB_EXTERN void @@ -247,13 +352,44 @@ hb_buffer_guess_segment_properties (hb_buffer_t *buffer); * of the text without the full context. * @HB_BUFFER_FLAG_EOT: flag indicating that special handling of the end of text * paragraph can be applied to this buffer, similar to - * @HB_BUFFER_FLAG_EOT. + * @HB_BUFFER_FLAG_BOT. * @HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES: * flag indication that character with Default_Ignorable * Unicode property should use the corresponding glyph - * from the font, instead of hiding them (currently done - * by replacing them with the space glyph and zeroing the - * advance width.) + * from the font, instead of hiding them (done by + * replacing them with the space glyph and zeroing the + * advance width.) This flag takes precedence over + * @HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES. + * @HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES: + * flag indication that character with Default_Ignorable + * Unicode property should be removed from glyph string + * instead of hiding them (done by replacing them with the + * space glyph and zeroing the advance width.) + * @HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES takes + * precedence over this flag. Since: 1.8.0 + * @HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE: + * flag indicating that a dotted circle should + * not be inserted in the rendering of incorrect + * character sequences (such at <0905 093E>). Since: 2.4.0 + * @HB_BUFFER_FLAG_VERIFY: + * flag indicating that the hb_shape() call and its variants + * should perform various verification processes on the results + * of the shaping operation on the buffer. If the verification + * fails, then either a buffer message is sent, if a message + * handler is installed on the buffer, or a message is written + * to standard error. In either case, the shaping result might + * be modified to show the failed output. Since: 3.4.0 + * @HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT: + * flag indicating that the @HB_GLYPH_FLAG_UNSAFE_TO_CONCAT + * glyph-flag should be produced by the shaper. By default + * it will not be produced since it incurs a cost. Since: 4.0.0 + * @HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL: + * flag indicating that the @HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL + * glyph-flag should be produced by the shaper. By default + * it will not be produced. Since: 5.1.0 + * @HB_BUFFER_FLAG_DEFINED: All currently defined flags: Since: 4.4.0 + * + * Flags for #hb_buffer_t. * * Since: 0.9.20 */ @@ -261,7 +397,14 @@ typedef enum { /*< flags >*/ HB_BUFFER_FLAG_DEFAULT = 0x00000000u, HB_BUFFER_FLAG_BOT = 0x00000001u, /* Beginning-of-text */ HB_BUFFER_FLAG_EOT = 0x00000002u, /* End-of-text */ - HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES = 0x00000004u + HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES = 0x00000004u, + HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES = 0x00000008u, + HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE = 0x00000010u, + HB_BUFFER_FLAG_VERIFY = 0x00000020u, + HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT = 0x00000040u, + HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL = 0x00000080u, + + HB_BUFFER_FLAG_DEFINED = 0x000000FFu } hb_buffer_flags_t; HB_EXTERN void @@ -269,9 +412,34 @@ hb_buffer_set_flags (hb_buffer_t *buffer, hb_buffer_flags_t flags); HB_EXTERN hb_buffer_flags_t -hb_buffer_get_flags (hb_buffer_t *buffer); +hb_buffer_get_flags (const 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. + * + * Data type for holding HarfBuzz's clustering behavior options. The cluster level + * dictates one aspect of how HarfBuzz will treat non-base characters + * during shaping. + * + * In @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES, non-base + * characters are merged into the cluster of the base character that precedes them. + * + * In @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS, non-base characters are initially + * assigned their own cluster values, which are not merged into preceding base + * clusters. This allows HarfBuzz to perform additional operations like reorder + * sequences of adjacent marks. + * + * @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES is the default, because it maintains + * backward compatibility with older versions of HarfBuzz. New client programs that + * do not need to maintain such backward compatibility are recommended to use + * @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS instead of the default. + * * Since: 0.9.42 */ typedef enum { @@ -286,7 +454,7 @@ hb_buffer_set_cluster_level (hb_buffer_t *buffer, hb_buffer_cluster_level_t cluster_level); HB_EXTERN hb_buffer_cluster_level_t -hb_buffer_get_cluster_level (hb_buffer_t *buffer); +hb_buffer_get_cluster_level (const hb_buffer_t *buffer); /** * HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT: @@ -303,18 +471,33 @@ hb_buffer_set_replacement_codepoint (hb_buffer_t *buffer, hb_codepoint_t replacement); HB_EXTERN hb_codepoint_t -hb_buffer_get_replacement_codepoint (hb_buffer_t *buffer); - +hb_buffer_get_replacement_codepoint (const hb_buffer_t *buffer); HB_EXTERN void -hb_buffer_reset (hb_buffer_t *buffer); +hb_buffer_set_invisible_glyph (hb_buffer_t *buffer, + hb_codepoint_t invisible); + +HB_EXTERN hb_codepoint_t +hb_buffer_get_invisible_glyph (const hb_buffer_t *buffer); + +HB_EXTERN void +hb_buffer_set_not_found_glyph (hb_buffer_t *buffer, + hb_codepoint_t not_found); + +HB_EXTERN hb_codepoint_t +hb_buffer_get_not_found_glyph (const hb_buffer_t *buffer); + + +/* + * Content API. + */ HB_EXTERN void hb_buffer_clear_contents (hb_buffer_t *buffer); HB_EXTERN hb_bool_t hb_buffer_pre_allocate (hb_buffer_t *buffer, - unsigned int size); + unsigned int size); HB_EXTERN hb_bool_t @@ -375,7 +558,7 @@ hb_buffer_add_codepoints (hb_buffer_t *buffer, HB_EXTERN void hb_buffer_append (hb_buffer_t *buffer, - hb_buffer_t *source, + const hb_buffer_t *source, unsigned int start, unsigned int end); @@ -384,17 +567,20 @@ hb_buffer_set_length (hb_buffer_t *buffer, unsigned int length); HB_EXTERN unsigned int -hb_buffer_get_length (hb_buffer_t *buffer); +hb_buffer_get_length (const hb_buffer_t *buffer); /* Getting glyphs out of the buffer */ HB_EXTERN hb_glyph_info_t * hb_buffer_get_glyph_infos (hb_buffer_t *buffer, - unsigned int *length); + unsigned int *length); HB_EXTERN hb_glyph_position_t * hb_buffer_get_glyph_positions (hb_buffer_t *buffer, - unsigned int *length); + unsigned int *length); + +HB_EXTERN hb_bool_t +hb_buffer_has_positions (hb_buffer_t *buffer); HB_EXTERN void @@ -412,6 +598,10 @@ hb_buffer_normalize_glyphs (hb_buffer_t *buffer); * @HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS: do not serialize glyph position information. * @HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES: do no serialize glyph name. * @HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS: serialize glyph extents. + * @HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS: serialize glyph flags. Since: 1.5.0 + * @HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES: do not serialize glyph advances, + * glyph offsets will reflect absolute glyph positions. Since: 1.8.0 + * @HB_BUFFER_SERIALIZE_FLAG_DEFINED: All currently defined flags. Since: 4.4.0 * * Flags that control what glyph information are serialized in hb_buffer_serialize_glyphs(). * @@ -423,7 +613,10 @@ typedef enum { /*< flags >*/ HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS = 0x00000002u, HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES = 0x00000004u, HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS = 0x00000008u, - HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS = 0x00000010u + HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS = 0x00000010u, + HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES = 0x00000020u, + + HB_BUFFER_SERIALIZE_FLAG_DEFINED = 0x0000003Fu } hb_buffer_serialize_flags_t; /** @@ -463,6 +656,27 @@ hb_buffer_serialize_glyphs (hb_buffer_t *buffer, hb_buffer_serialize_format_t format, hb_buffer_serialize_flags_t flags); +HB_EXTERN unsigned int +hb_buffer_serialize_unicode (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, + hb_buffer_serialize_format_t format, + hb_buffer_serialize_flags_t flags); + +HB_EXTERN unsigned int +hb_buffer_serialize (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, + hb_font_t *font, + hb_buffer_serialize_format_t format, + hb_buffer_serialize_flags_t flags); + HB_EXTERN hb_bool_t hb_buffer_deserialize_glyphs (hb_buffer_t *buffer, const char *buf, @@ -471,11 +685,48 @@ hb_buffer_deserialize_glyphs (hb_buffer_t *buffer, hb_font_t *font, hb_buffer_serialize_format_t format); +HB_EXTERN hb_bool_t +hb_buffer_deserialize_unicode (hb_buffer_t *buffer, + const char *buf, + int buf_len, + const char **end_ptr, + hb_buffer_serialize_format_t format); + + /* * Compare buffers */ +/** + * hb_buffer_diff_flags_t: + * @HB_BUFFER_DIFF_FLAG_EQUAL: equal buffers. + * @HB_BUFFER_DIFF_FLAG_CONTENT_TYPE_MISMATCH: buffers with different + * #hb_buffer_content_type_t. + * @HB_BUFFER_DIFF_FLAG_LENGTH_MISMATCH: buffers with differing length. + * @HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT: `.notdef` glyph is present in the + * reference buffer. + * @HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT: dotted circle glyph is present + * in the reference buffer. + * @HB_BUFFER_DIFF_FLAG_CODEPOINT_MISMATCH: difference in #hb_glyph_info_t.codepoint + * @HB_BUFFER_DIFF_FLAG_CLUSTER_MISMATCH: difference in #hb_glyph_info_t.cluster + * @HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH: difference in #hb_glyph_flags_t. + * @HB_BUFFER_DIFF_FLAG_POSITION_MISMATCH: difference in #hb_glyph_position_t. + * + * Flags from comparing two #hb_buffer_t's. + * + * Buffer with different #hb_buffer_content_type_t cannot be meaningfully + * compared in any further detail. + * + * For buffers with differing length, the per-glyph comparison is not + * attempted, though we do still scan reference buffer for dotted circle and + * `.notdef` glyphs. + * + * If the buffers have the same length, we compare them glyph-by-glyph and + * report which aspect(s) of the glyph info/position are different. + * + * Since: 1.5.0 + */ typedef enum { /*< flags >*/ HB_BUFFER_DIFF_FLAG_EQUAL = 0x0000, @@ -512,9 +763,26 @@ hb_buffer_diff (hb_buffer_t *buffer, /* - * Debugging. + * Tracing. */ +/** + * hb_buffer_message_func_t: + * @buffer: An #hb_buffer_t to work upon + * @font: The #hb_font_t the @buffer is shaped with + * @message: `NULL`-terminated message passed to the function + * @user_data: User data pointer passed by the caller + * + * A callback method for #hb_buffer_t. The method gets called with the + * #hb_buffer_t it was set on, the #hb_font_t the buffer is shaped with and a + * message describing what step of the shaping process will be performed. + * Returning `false` from this method will skip this shaping step and move to + * the next one. + * + * Return value: `true` to perform the shaping step, `false` to skip it. + * + * Since: 1.1.3 + */ typedef hb_bool_t (*hb_buffer_message_func_t) (hb_buffer_t *buffer, hb_font_t *font, const char *message, diff --git a/src/3rdparty/harfbuzz-ng/src/hb-buffer.hh b/src/3rdparty/harfbuzz-ng/src/hb-buffer.hh new file mode 100644 index 00000000000..f04ad58f116 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-buffer.hh @@ -0,0 +1,681 @@ +/* + * Copyright © 1998-2004 David Turner and Werner Lemberg + * Copyright © 2004,2007,2009,2010 Red Hat, Inc. + * Copyright © 2011,2012 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. + * + * Red Hat Author(s): Owen Taylor, Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_BUFFER_HH +#define HB_BUFFER_HH + +#include "hb.hh" +#include "hb-unicode.hh" +#include "hb-set-digest.hh" + + +static_assert ((sizeof (hb_glyph_info_t) == 20), ""); +static_assert ((sizeof (hb_glyph_info_t) == sizeof (hb_glyph_position_t)), ""); + +HB_MARK_AS_FLAG_T (hb_glyph_flags_t); +HB_MARK_AS_FLAG_T (hb_buffer_flags_t); +HB_MARK_AS_FLAG_T (hb_buffer_serialize_flags_t); +HB_MARK_AS_FLAG_T (hb_buffer_diff_flags_t); + +enum hb_buffer_scratch_flags_t { + HB_BUFFER_SCRATCH_FLAG_DEFAULT = 0x00000000u, + HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII = 0x00000001u, + HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES = 0x00000002u, + HB_BUFFER_SCRATCH_FLAG_HAS_SPACE_FALLBACK = 0x00000004u, + HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT = 0x00000008u, + HB_BUFFER_SCRATCH_FLAG_HAS_CGJ = 0x00000010u, + HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS = 0x00000020u, + HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE = 0x00000040u, + + /* Reserved for shapers' internal use. */ + HB_BUFFER_SCRATCH_FLAG_SHAPER0 = 0x01000000u, + HB_BUFFER_SCRATCH_FLAG_SHAPER1 = 0x02000000u, + HB_BUFFER_SCRATCH_FLAG_SHAPER2 = 0x04000000u, + HB_BUFFER_SCRATCH_FLAG_SHAPER3 = 0x08000000u, +}; +HB_MARK_AS_FLAG_T (hb_buffer_scratch_flags_t); + + +/* + * hb_buffer_t + */ + +struct hb_buffer_t +{ + hb_object_header_t header; + + /* + * Information about how the text in the buffer should be treated. + */ + + hb_unicode_funcs_t *unicode; /* Unicode functions */ + hb_buffer_flags_t flags; /* BOT / EOT / etc. */ + hb_buffer_cluster_level_t cluster_level; + hb_codepoint_t replacement; /* U+FFFD or something else. */ + hb_codepoint_t invisible; /* 0 or something else. */ + hb_codepoint_t not_found; /* 0 or something else. */ + + /* + * Buffer contents + */ + + hb_buffer_content_type_t content_type; + hb_segment_properties_t props; /* Script, language, direction */ + + bool successful; /* Allocations successful */ + bool shaping_failed; /* Shaping failure */ + bool have_output; /* Whether we have an output buffer going on */ + bool have_positions; /* Whether we have positions */ + + unsigned int idx; /* Cursor into ->info and ->pos arrays */ + unsigned int len; /* Length of ->info and ->pos arrays */ + unsigned int out_len; /* Length of ->out_info array if have_output */ + + unsigned int allocated; /* Length of allocated arrays */ + hb_glyph_info_t *info; + hb_glyph_info_t *out_info; + hb_glyph_position_t *pos; + + /* Text before / after the main buffer contents. + * Always in Unicode, and ordered outward. + * Index 0 is for "pre-context", 1 for "post-context". */ + static constexpr unsigned CONTEXT_LENGTH = 5u; + hb_codepoint_t context[2][CONTEXT_LENGTH]; + unsigned int context_len[2]; + + + /* + * Managed by enter / leave + */ + + uint8_t allocated_var_bits; + uint8_t serial; + hb_buffer_scratch_flags_t scratch_flags; /* Have space-fallback, etc. */ + unsigned int max_len; /* Maximum allowed len. */ + int max_ops; /* Maximum allowed operations. */ + /* The bits here reflect current allocations of the bytes in glyph_info_t's var1 and var2. */ + + + /* + * Messaging callback + */ + +#ifndef HB_NO_BUFFER_MESSAGE + hb_buffer_message_func_t message_func; + void *message_data; + hb_destroy_func_t message_destroy; + unsigned message_depth; /* How deeply are we inside a message callback? */ +#else + static constexpr unsigned message_depth = 0u; +#endif + + + + /* Methods */ + + HB_NODISCARD bool in_error () const { return !successful; } + + void allocate_var (unsigned int start, unsigned int count) + { + unsigned int end = start + count; + assert (end <= 8); + unsigned int bits = (1u< (info, len).reverse (start, end); + if (have_positions) + hb_array_t (pos, len).reverse (start, end); + } + void reverse () { reverse_range (0, len); } + + template + void reverse_groups (const FuncType& group, + bool merge_clusters = false) + { + if (unlikely (!len)) + return; + + unsigned start = 0; + unsigned i; + for (i = 1; i < len; i++) + { + if (!group (info[i - 1], info[i])) + { + if (merge_clusters) + this->merge_clusters (start, i); + reverse_range (start, i); + start = i; + } + } + if (merge_clusters) + this->merge_clusters (start, i); + reverse_range (start, i); + + reverse (); + } + + template + unsigned group_end (unsigned start, const FuncType& group) const + { + while (++start < len && group (info[start - 1], info[start])) + ; + + return start; + } + + static bool _cluster_group_func (const hb_glyph_info_t& a, + const hb_glyph_info_t& b) + { return a.cluster == b.cluster; } + + void reverse_clusters () { reverse_groups (_cluster_group_func); } + + HB_INTERNAL void guess_segment_properties (); + + HB_INTERNAL bool sync (); + HB_INTERNAL int sync_so_far (); + HB_INTERNAL void clear_output (); + HB_INTERNAL void clear_positions (); + + template + HB_NODISCARD bool replace_glyphs (unsigned int num_in, + unsigned int num_out, + const T *glyph_data) + { + if (unlikely (!make_room_for (num_in, num_out))) return false; + + assert (idx + num_in <= len); + + merge_clusters (idx, idx + num_in); + + hb_glyph_info_t &orig_info = idx < len ? cur() : prev(); + + hb_glyph_info_t *pinfo = &out_info[out_len]; + for (unsigned int i = 0; i < num_out; i++) + { + *pinfo = orig_info; + pinfo->codepoint = glyph_data[i]; + pinfo++; + } + + idx += num_in; + out_len += num_out; + return true; + } + + HB_NODISCARD bool replace_glyph (hb_codepoint_t glyph_index) + { return replace_glyphs (1, 1, &glyph_index); } + + /* Makes a copy of the glyph at idx to output and replace glyph_index */ + HB_NODISCARD bool output_glyph (hb_codepoint_t glyph_index) + { return replace_glyphs (0, 1, &glyph_index); } + + HB_NODISCARD bool output_info (const hb_glyph_info_t &glyph_info) + { + if (unlikely (!make_room_for (0, 1))) return false; + + out_info[out_len] = glyph_info; + + out_len++; + return true; + } + /* Copies glyph at idx to output but doesn't advance idx */ + HB_NODISCARD bool copy_glyph () + { + /* Extra copy because cur()'s return can be freed within + * output_info() call if buffer reallocates. */ + return output_info (hb_glyph_info_t (cur())); + } + + /* Copies glyph at idx to output and advance idx. + * If there's no output, just advance idx. */ + HB_NODISCARD bool next_glyph () + { + if (have_output) + { + if (out_info != info || out_len != idx) + { + if (unlikely (!make_room_for (1, 1))) return false; + out_info[out_len] = info[idx]; + } + out_len++; + } + + idx++; + return true; + } + /* Copies n glyphs at idx to output and advance idx. + * If there's no output, just advance idx. */ + HB_NODISCARD bool next_glyphs (unsigned int n) + { + if (have_output) + { + if (out_info != info || out_len != idx) + { + if (unlikely (!make_room_for (n, n))) return false; + memmove (out_info + out_len, info + idx, n * sizeof (out_info[0])); + } + out_len += n; + } + + idx += n; + return true; + } + /* Advance idx without copying to output. */ + void skip_glyph () { idx++; } + void reset_masks (hb_mask_t mask) + { + for (unsigned int j = 0; j < len; j++) + info[j].mask = mask; + } + void add_masks (hb_mask_t mask) + { + for (unsigned int j = 0; j < len; j++) + info[j].mask |= mask; + } + HB_INTERNAL void set_masks (hb_mask_t value, hb_mask_t mask, + unsigned int cluster_start, unsigned int cluster_end); + + void merge_clusters (unsigned int start, unsigned int end) + { + if (end - start < 2) + return; + merge_clusters_impl (start, end); + } + HB_INTERNAL void merge_clusters_impl (unsigned int start, unsigned int end); + HB_INTERNAL void merge_out_clusters (unsigned int start, unsigned int end); + /* Merge clusters for deleting current glyph, and skip it. */ + HB_INTERNAL void delete_glyph (); + HB_INTERNAL void delete_glyphs_inplace (bool (*filter) (const hb_glyph_info_t *info)); + + + + /* Adds glyph flags in mask to infos with clusters between start and end. + * The start index will be from out-buffer if from_out_buffer is true. + * If interior is true, then the cluster having the minimum value is skipped. */ + void _set_glyph_flags (hb_mask_t mask, + unsigned start = 0, + unsigned end = (unsigned) -1, + bool interior = false, + bool from_out_buffer = false) + { + end = hb_min (end, len); + + if (interior && !from_out_buffer && end - start < 2) + return; + + scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS; + + if (!from_out_buffer || !have_output) + { + if (!interior) + { + for (unsigned i = start; i < end; i++) + info[i].mask |= mask; + } + else + { + unsigned cluster = _infos_find_min_cluster (info, start, end); + _infos_set_glyph_flags (info, start, end, cluster, mask); + } + } + else + { + assert (start <= out_len); + assert (idx <= end); + + if (!interior) + { + for (unsigned i = start; i < out_len; i++) + out_info[i].mask |= mask; + for (unsigned i = idx; i < end; i++) + info[i].mask |= mask; + } + else + { + unsigned cluster = _infos_find_min_cluster (info, idx, end); + cluster = _infos_find_min_cluster (out_info, start, out_len, cluster); + + _infos_set_glyph_flags (out_info, start, out_len, cluster, mask); + _infos_set_glyph_flags (info, idx, end, cluster, mask); + } + } + } + + void unsafe_to_break (unsigned int start = 0, unsigned int end = -1) + { + _set_glyph_flags (HB_GLYPH_FLAG_UNSAFE_TO_BREAK | HB_GLYPH_FLAG_UNSAFE_TO_CONCAT, + start, end, + true); + } + void safe_to_insert_tatweel (unsigned int start = 0, unsigned int end = -1) + { + if ((flags & HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL) == 0) + { + unsafe_to_break (start, end); + return; + } + _set_glyph_flags (HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL, + start, end, + true); + } +#ifndef HB_OPTIMIZE_SIZE + HB_ALWAYS_INLINE +#endif + void unsafe_to_concat (unsigned int start = 0, unsigned int end = -1) + { + if (likely ((flags & HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT) == 0)) + return; + _set_glyph_flags (HB_GLYPH_FLAG_UNSAFE_TO_CONCAT, + start, end, + false); + } + void unsafe_to_break_from_outbuffer (unsigned int start = 0, unsigned int end = -1) + { + _set_glyph_flags (HB_GLYPH_FLAG_UNSAFE_TO_BREAK | HB_GLYPH_FLAG_UNSAFE_TO_CONCAT, + start, end, + true, true); + } +#ifndef HB_OPTIMIZE_SIZE + HB_ALWAYS_INLINE +#endif + void unsafe_to_concat_from_outbuffer (unsigned int start = 0, unsigned int end = -1) + { + if (likely ((flags & HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT) == 0)) + return; + _set_glyph_flags (HB_GLYPH_FLAG_UNSAFE_TO_CONCAT, + start, end, + false, true); + } + + + /* Internal methods */ + HB_NODISCARD HB_INTERNAL bool move_to (unsigned int i); /* i is output-buffer index. */ + + HB_NODISCARD HB_INTERNAL bool enlarge (unsigned int size); + + HB_NODISCARD bool resize (unsigned length) + { + assert (!have_output); + if (unlikely (!ensure (length))) return false; + len = length; + return true; + } + HB_NODISCARD bool ensure (unsigned int size) + { return likely (!size || size < allocated) ? true : enlarge (size); } + + HB_NODISCARD bool ensure_inplace (unsigned int size) + { return likely (!size || size < allocated); } + + void assert_glyphs () + { + assert ((content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS) || + (!len && (content_type == HB_BUFFER_CONTENT_TYPE_INVALID))); + } + void assert_unicode () + { + assert ((content_type == HB_BUFFER_CONTENT_TYPE_UNICODE) || + (!len && (content_type == HB_BUFFER_CONTENT_TYPE_INVALID))); + } + HB_NODISCARD bool ensure_glyphs () + { + if (unlikely (content_type != HB_BUFFER_CONTENT_TYPE_GLYPHS)) + { + if (content_type != HB_BUFFER_CONTENT_TYPE_INVALID) + return false; + assert (len == 0); + content_type = HB_BUFFER_CONTENT_TYPE_GLYPHS; + } + return true; + } + HB_NODISCARD bool ensure_unicode () + { + if (unlikely (content_type != HB_BUFFER_CONTENT_TYPE_UNICODE)) + { + if (content_type != HB_BUFFER_CONTENT_TYPE_INVALID) + return false; + assert (len == 0); + content_type = HB_BUFFER_CONTENT_TYPE_UNICODE; + } + return true; + } + + HB_NODISCARD HB_INTERNAL bool make_room_for (unsigned int num_in, unsigned int num_out); + HB_NODISCARD HB_INTERNAL bool shift_forward (unsigned int count); + + typedef long scratch_buffer_t; + HB_INTERNAL scratch_buffer_t *get_scratch_buffer (unsigned int *size); + + void clear_context (unsigned int side) { context_len[side] = 0; } + + HB_INTERNAL void sort (unsigned int start, unsigned int end, int(*compar)(const hb_glyph_info_t *, const hb_glyph_info_t *)); + + bool messaging () + { +#ifdef HB_NO_BUFFER_MESSAGE + return false; +#else + return unlikely (message_func); +#endif + } + bool message (hb_font_t *font, const char *fmt, ...) HB_PRINTF_FUNC(3, 4) + { +#ifdef HB_NO_BUFFER_MESSAGE + return true; +#else + if (likely (!messaging ())) + return true; + + va_list ap; + va_start (ap, fmt); + bool ret = message_impl (font, fmt, ap); + va_end (ap); + + return ret; +#endif + } + HB_INTERNAL bool message_impl (hb_font_t *font, const char *fmt, va_list ap) HB_PRINTF_FUNC(3, 0); + + static void + set_cluster (hb_glyph_info_t &inf, unsigned int cluster, unsigned int mask = 0) + { + if (inf.cluster != cluster) + inf.mask = (inf.mask & ~HB_GLYPH_FLAG_DEFINED) | (mask & HB_GLYPH_FLAG_DEFINED); + inf.cluster = cluster; + } + void + _infos_set_glyph_flags (hb_glyph_info_t *infos, + unsigned int start, unsigned int end, + unsigned int cluster, + hb_mask_t mask) + { + if (unlikely (start == end)) + return; + + unsigned cluster_first = infos[start].cluster; + unsigned cluster_last = infos[end - 1].cluster; + + if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_CHARACTERS || + (cluster != cluster_first && cluster != cluster_last)) + { + for (unsigned int i = start; i < end; i++) + if (cluster != infos[i].cluster) + { + scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS; + infos[i].mask |= mask; + } + return; + } + + /* Monotone clusters */ + + if (cluster == cluster_first) + { + for (unsigned int i = end; start < i && infos[i - 1].cluster != cluster_first; i--) + { + scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS; + infos[i - 1].mask |= mask; + } + } + else /* cluster == cluster_last */ + { + for (unsigned int i = start; i < end && infos[i].cluster != cluster_last; i++) + { + scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS; + infos[i].mask |= mask; + } + } + } + unsigned + _infos_find_min_cluster (const hb_glyph_info_t *infos, + unsigned start, unsigned end, + unsigned cluster = UINT_MAX) + { + if (unlikely (start == end)) + return cluster; + + if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_CHARACTERS) + { + for (unsigned int i = start; i < end; i++) + cluster = hb_min (cluster, infos[i].cluster); + return cluster; + } + + return hb_min (cluster, hb_min (infos[start].cluster, infos[end - 1].cluster)); + } + + void clear_glyph_flags (hb_mask_t mask = 0) + { + for (unsigned int i = 0; i < len; i++) + info[i].mask = (info[i].mask & ~HB_GLYPH_FLAG_DEFINED) | (mask & HB_GLYPH_FLAG_DEFINED); + } +}; +DECLARE_NULL_INSTANCE (hb_buffer_t); + + +#define foreach_group(buffer, start, end, group_func) \ + for (unsigned int \ + _count = buffer->len, \ + start = 0, end = _count ? buffer->group_end (0, group_func) : 0; \ + start < _count; \ + start = end, end = buffer->group_end (start, group_func)) + +#define foreach_cluster(buffer, start, end) \ + foreach_group (buffer, start, end, hb_buffer_t::_cluster_group_func) + + +#define HB_BUFFER_XALLOCATE_VAR(b, func, var) \ + b->func (offsetof (hb_glyph_info_t, var) - offsetof(hb_glyph_info_t, var1), \ + sizeof (b->info[0].var)) +#define HB_BUFFER_ALLOCATE_VAR(b, var) HB_BUFFER_XALLOCATE_VAR (b, allocate_var, var ()) +#define HB_BUFFER_TRY_ALLOCATE_VAR(b, var) HB_BUFFER_XALLOCATE_VAR (b, try_allocate_var, var ()) +#define HB_BUFFER_DEALLOCATE_VAR(b, var) HB_BUFFER_XALLOCATE_VAR (b, deallocate_var, var ()) +#define HB_BUFFER_ASSERT_VAR(b, var) HB_BUFFER_XALLOCATE_VAR (b, assert_var, var ()) + + +#endif /* HB_BUFFER_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-cache-private.hh b/src/3rdparty/harfbuzz-ng/src/hb-cache.hh similarity index 50% rename from src/3rdparty/harfbuzz-ng/src/hb-cache-private.hh rename to src/3rdparty/harfbuzz-ng/src/hb-cache.hh index 24957e1e96f..6d8a54cf102 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-cache-private.hh +++ b/src/3rdparty/harfbuzz-ng/src/hb-cache.hh @@ -24,36 +24,64 @@ * Google Author(s): Behdad Esfahbod */ -#ifndef HB_CACHE_PRIVATE_HH -#define HB_CACHE_PRIVATE_HH +#ifndef HB_CACHE_HH +#define HB_CACHE_HH -#include "hb-private.hh" +#include "hb.hh" -/* Implements a lock-free cache for int->int functions. */ +/* Implements a lockfree cache for int->int functions. + * + * The cache is a fixed-size array of 16-bit or 32-bit integers. + * The key is split into two parts: the cache index and the rest. + * + * The cache index is used to index into the array. The rest is used + * to store the key and the value. + * + * The value is stored in the least significant bits of the integer. + * The key is stored in the most significant bits of the integer. + * The key is shifted by cache_bits to the left to make room for the + * value. + */ -template +template struct hb_cache_t { - ASSERT_STATIC (key_bits >= cache_bits); - ASSERT_STATIC (key_bits + value_bits - cache_bits < 8 * sizeof (unsigned int)); + using item_t = typename std::conditional::type, + typename std::conditional::type + >::type; - inline void clear (void) + static_assert ((key_bits >= cache_bits), ""); + static_assert ((key_bits + value_bits <= cache_bits + 8 * sizeof (item_t)), ""); + + hb_cache_t () { clear (); } + + void clear () { - memset (values, 255, sizeof (values)); + for (auto &v : values) + v = -1; } - inline bool get (unsigned int key, unsigned int *value) + bool get (unsigned int key, unsigned int *value) const { unsigned int k = key & ((1u<> value_bits) != (key >> cache_bits)) + if ((key_bits + value_bits - cache_bits == 8 * sizeof (item_t) && v == (unsigned int) -1) || + (v >> value_bits) != (key >> cache_bits)) return false; *value = v & ((1u<> key_bits) || (value >> value_bits))) return false; /* Overflows */ @@ -64,11 +92,8 @@ struct hb_cache_t } private: - unsigned int values[1u< hb_cmap_cache_t; -typedef hb_cache_t<16, 24, 8> hb_advance_cache_t; - -#endif /* HB_CACHE_PRIVATE_HH */ +#endif /* HB_CACHE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-cairo-utils.cc b/src/3rdparty/harfbuzz-ng/src/hb-cairo-utils.cc new file mode 100644 index 00000000000..ec1499e861e --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-cairo-utils.cc @@ -0,0 +1,874 @@ +/* + * Copyright © 2022 Red Hat, Inc + * Copyright © 2021, 2022 Black Foundry + * + * 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): Matthias Clasen + */ + +#include "hb.hh" + +#ifdef HAVE_CAIRO + +#include "hb-cairo-utils.hh" + +#include + +/* Some routines in this file were ported from BlackRenderer by Black Foundry. + * Used by permission to relicense to HarfBuzz license. + * + * https://github.com/BlackFoundryCom/black-renderer + */ + +#define PREALLOCATED_COLOR_STOPS 16 + +typedef struct { + float r, g, b, a; +} hb_cairo_color_t; + +static inline cairo_extend_t +hb_cairo_extend (hb_paint_extend_t extend) +{ + switch (extend) + { + case HB_PAINT_EXTEND_PAD: return CAIRO_EXTEND_PAD; + case HB_PAINT_EXTEND_REPEAT: return CAIRO_EXTEND_REPEAT; + case HB_PAINT_EXTEND_REFLECT: return CAIRO_EXTEND_REFLECT; + default: break; + } + + return CAIRO_EXTEND_PAD; +} + +#ifdef CAIRO_HAS_PNG_FUNCTIONS +typedef struct +{ + hb_blob_t *blob; + unsigned int offset; +} hb_cairo_read_blob_data_t; + +static cairo_status_t +hb_cairo_read_blob (void *closure, + unsigned char *data, + unsigned int length) +{ + hb_cairo_read_blob_data_t *r = (hb_cairo_read_blob_data_t *) closure; + const char *d; + unsigned int size; + + d = hb_blob_get_data (r->blob, &size); + + if (r->offset + length > size) + return CAIRO_STATUS_READ_ERROR; + + hb_memcpy (data, d + r->offset, length); + r->offset += length; + + return CAIRO_STATUS_SUCCESS; +} +#endif + +static const cairo_user_data_key_t *_hb_cairo_surface_blob_user_data_key = {0}; + +static void +_hb_cairo_destroy_blob (void *p) +{ + hb_blob_destroy ((hb_blob_t *) p); +} + +hb_bool_t +_hb_cairo_paint_glyph_image (hb_cairo_context_t *c, + hb_blob_t *blob, + unsigned width, + unsigned height, + hb_tag_t format, + float slant, + hb_glyph_extents_t *extents) +{ + cairo_t *cr = c->cr; + + if (!extents) /* SVG currently. */ + return false; + + cairo_surface_t *surface = nullptr; + +#ifdef CAIRO_HAS_PNG_FUNCTIONS + if (format == HB_PAINT_IMAGE_FORMAT_PNG) + { + hb_cairo_read_blob_data_t r; + r.blob = blob; + r.offset = 0; + surface = cairo_image_surface_create_from_png_stream (hb_cairo_read_blob, &r); + + /* For PNG, width,height can be unreliable, as is the case for NotoColorEmoji :(. + * Just pull them out of the surface. */ + width = cairo_image_surface_get_width (surface); + height = cairo_image_surface_get_width (surface); + } + else +#endif + if (format == HB_PAINT_IMAGE_FORMAT_BGRA) + { + /* Byte-endian conversion. */ + unsigned data_size = hb_blob_get_length (blob); + if (data_size < width * height * 4) + return false; + + unsigned char *data; +#ifdef __BYTE_ORDER + if (__BYTE_ORDER == __BIG_ENDIAN) + { + data = (unsigned char *) hb_blob_get_data_writable (blob, nullptr); + if (!data) + return false; + + unsigned count = width * height * 4; + for (unsigned i = 0; i < count; i += 4) + { + unsigned char b; + b = data[i]; + data[i] = data[i+3]; + data[i+3] = b; + b = data[i+1]; + data[i+1] = data[i+2]; + data[i+2] = b; + } + } + else +#endif + data = (unsigned char *) hb_blob_get_data (blob, nullptr); + + surface = cairo_image_surface_create_for_data (data, + CAIRO_FORMAT_ARGB32, + width, height, + width * 4); + + cairo_surface_set_user_data (surface, + _hb_cairo_surface_blob_user_data_key, + hb_blob_reference (blob), + _hb_cairo_destroy_blob); + } + + if (!surface) + return false; + + cairo_save (cr); + /* this clip is here to work around recording surface limitations */ + cairo_rectangle (cr, + extents->x_bearing, + extents->y_bearing, + extents->width, + extents->height); + cairo_clip (cr); + + cairo_pattern_t *pattern = cairo_pattern_create_for_surface (surface); + cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD); + + cairo_matrix_t matrix = {(double) width, 0, 0, (double) height, 0, 0}; + cairo_pattern_set_matrix (pattern, &matrix); + + /* Undo slant in the extents and apply it in the context. */ + extents->width -= extents->height * slant; + extents->x_bearing -= extents->y_bearing * slant; + cairo_matrix_t cairo_matrix = {1., 0., (double) slant, 1., 0., 0.}; + cairo_transform (cr, &cairo_matrix); + + cairo_translate (cr, extents->x_bearing, extents->y_bearing); + cairo_scale (cr, extents->width, extents->height); + cairo_set_source (cr, pattern); + + cairo_paint (cr); + + cairo_pattern_destroy (pattern); + cairo_surface_destroy (surface); + + cairo_restore (cr); + + return true; +} + +static void +_hb_cairo_reduce_anchors (float x0, float y0, + float x1, float y1, + float x2, float y2, + float *xx0, float *yy0, + float *xx1, float *yy1) +{ + float q1x, q1y, q2x, q2y; + float s; + float k; + + q2x = x2 - x0; + q2y = y2 - y0; + q1x = x1 - x0; + q1y = y1 - y0; + + s = q2x * q2x + q2y * q2y; + if (s < 0.000001f) + { + *xx0 = x0; *yy0 = y0; + *xx1 = x1; *yy1 = y1; + return; + } + + k = (q2x * q1x + q2y * q1y) / s; + *xx0 = x0; + *yy0 = y0; + *xx1 = x1 - k * q2x; + *yy1 = y1 - k * q2y; +} + +static int +_hb_cairo_cmp_color_stop (const void *p1, + const void *p2) +{ + const hb_color_stop_t *c1 = (const hb_color_stop_t *) p1; + const hb_color_stop_t *c2 = (const hb_color_stop_t *) p2; + + if (c1->offset < c2->offset) + return -1; + else if (c1->offset > c2->offset) + return 1; + else + return 0; +} + +static void +_hb_cairo_normalize_color_line (hb_color_stop_t *stops, + unsigned int len, + float *omin, + float *omax) +{ + float min, max; + + hb_qsort (stops, len, sizeof (hb_color_stop_t), _hb_cairo_cmp_color_stop); + + min = max = stops[0].offset; + for (unsigned int i = 0; i < len; i++) + { + min = hb_min (min, stops[i].offset); + max = hb_max (max, stops[i].offset); + } + + if (min != max) + { + for (unsigned int i = 0; i < len; i++) + stops[i].offset = (stops[i].offset - min) / (max - min); + } + + *omin = min; + *omax = max; +} + +static bool +_hb_cairo_get_color_stops (hb_cairo_context_t *c, + hb_color_line_t *color_line, + unsigned *count, + hb_color_stop_t **stops) +{ + unsigned len = hb_color_line_get_color_stops (color_line, 0, nullptr, nullptr); + if (len > *count) + { + *stops = (hb_color_stop_t *) hb_malloc (len * sizeof (hb_color_stop_t)); + if (unlikely (!stops)) + return false; + } + hb_color_line_get_color_stops (color_line, 0, &len, *stops); + for (unsigned i = 0; i < len; i++) + if ((*stops)[i].is_foreground) + { +#ifdef HAVE_CAIRO_USER_SCALED_FONT_GET_FOREGROUND_SOURCE + double r, g, b, a; + cairo_pattern_t *foreground = cairo_user_scaled_font_get_foreground_source (c->scaled_font); + if (cairo_pattern_get_rgba (foreground, &r, &g, &b, &a) == CAIRO_STATUS_SUCCESS) + (*stops)[i].color = HB_COLOR (round (b * 255.), round (g * 255.), round (r * 255.), + round (a * hb_color_get_alpha ((*stops)[i].color))); + else +#endif + (*stops)[i].color = HB_COLOR (0, 0, 0, hb_color_get_alpha ((*stops)[i].color)); + } + + *count = len; + return true; +} + +void +_hb_cairo_paint_linear_gradient (hb_cairo_context_t *c, + hb_color_line_t *color_line, + float x0, float y0, + float x1, float y1, + float x2, float y2) +{ + cairo_t *cr = c->cr; + + unsigned int len = PREALLOCATED_COLOR_STOPS; + hb_color_stop_t stops_[PREALLOCATED_COLOR_STOPS]; + hb_color_stop_t *stops = stops_; + float xx0, yy0, xx1, yy1; + float xxx0, yyy0, xxx1, yyy1; + float min, max; + cairo_pattern_t *pattern; + + if (unlikely (!_hb_cairo_get_color_stops (c, color_line, &len, &stops))) + return; + _hb_cairo_normalize_color_line (stops, len, &min, &max); + + _hb_cairo_reduce_anchors (x0, y0, x1, y1, x2, y2, &xx0, &yy0, &xx1, &yy1); + + xxx0 = xx0 + min * (xx1 - xx0); + yyy0 = yy0 + min * (yy1 - yy0); + xxx1 = xx0 + max * (xx1 - xx0); + yyy1 = yy0 + max * (yy1 - yy0); + + pattern = cairo_pattern_create_linear ((double) xxx0, (double) yyy0, (double) xxx1, (double) yyy1); + cairo_pattern_set_extend (pattern, hb_cairo_extend (hb_color_line_get_extend (color_line))); + for (unsigned int i = 0; i < len; i++) + { + double r, g, b, a; + r = hb_color_get_red (stops[i].color) / 255.; + g = hb_color_get_green (stops[i].color) / 255.; + b = hb_color_get_blue (stops[i].color) / 255.; + a = hb_color_get_alpha (stops[i].color) / 255.; + cairo_pattern_add_color_stop_rgba (pattern, (double) stops[i].offset, r, g, b, a); + } + + cairo_set_source (cr, pattern); + cairo_paint (cr); + + cairo_pattern_destroy (pattern); + + if (stops != stops_) + hb_free (stops); +} + +void +_hb_cairo_paint_radial_gradient (hb_cairo_context_t *c, + hb_color_line_t *color_line, + float x0, float y0, float r0, + float x1, float y1, float r1) +{ + cairo_t *cr = c->cr; + + unsigned int len = PREALLOCATED_COLOR_STOPS; + hb_color_stop_t stops_[PREALLOCATED_COLOR_STOPS]; + hb_color_stop_t *stops = stops_; + float min, max; + float xx0, yy0, xx1, yy1; + float rr0, rr1; + cairo_pattern_t *pattern; + + if (unlikely (!_hb_cairo_get_color_stops (c, color_line, &len, &stops))) + return; + _hb_cairo_normalize_color_line (stops, len, &min, &max); + + xx0 = x0 + min * (x1 - x0); + yy0 = y0 + min * (y1 - y0); + xx1 = x0 + max * (x1 - x0); + yy1 = y0 + max * (y1 - y0); + rr0 = r0 + min * (r1 - r0); + rr1 = r0 + max * (r1 - r0); + + pattern = cairo_pattern_create_radial ((double) xx0, (double) yy0, (double) rr0, (double) xx1, (double) yy1, (double) rr1); + cairo_pattern_set_extend (pattern, hb_cairo_extend (hb_color_line_get_extend (color_line))); + + for (unsigned int i = 0; i < len; i++) + { + double r, g, b, a; + r = hb_color_get_red (stops[i].color) / 255.; + g = hb_color_get_green (stops[i].color) / 255.; + b = hb_color_get_blue (stops[i].color) / 255.; + a = hb_color_get_alpha (stops[i].color) / 255.; + cairo_pattern_add_color_stop_rgba (pattern, (double) stops[i].offset, r, g, b, a); + } + + cairo_set_source (cr, pattern); + cairo_paint (cr); + + cairo_pattern_destroy (pattern); + + if (stops != stops_) + hb_free (stops); +} + +typedef struct { + float x, y; +} hb_cairo_point_t; + +static inline float +_hb_cairo_interpolate (float f0, float f1, float f) +{ + return f0 + f * (f1 - f0); +} + +static inline void +_hb_cairo_premultiply (hb_cairo_color_t *c) +{ + c->r *= c->a; + c->g *= c->a; + c->b *= c->a; +} + +static inline void +_hb_cairo_unpremultiply (hb_cairo_color_t *c) +{ + if (c->a != 0.f) + { + c->r /= c->a; + c->g /= c->a; + c->b /= c->a; + } +} + +static void +_hb_cairo_interpolate_colors (hb_cairo_color_t *c0, hb_cairo_color_t *c1, float k, hb_cairo_color_t *c) +{ + // According to the COLR specification, gradients + // should be interpolated in premultiplied form + _hb_cairo_premultiply (c0); + _hb_cairo_premultiply (c1); + c->r = c0->r + k * (c1->r - c0->r); + c->g = c0->g + k * (c1->g - c0->g); + c->b = c0->b + k * (c1->b - c0->b); + c->a = c0->a + k * (c1->a - c0->a); + _hb_cairo_unpremultiply (c); +} + +static inline float +_hb_cairo_dot (hb_cairo_point_t p, hb_cairo_point_t q) +{ + return p.x * q.x + p.y * q.y; +} + +static inline hb_cairo_point_t +_hb_cairo_normalize (hb_cairo_point_t p) +{ + float len = sqrtf (_hb_cairo_dot (p, p)); + + return hb_cairo_point_t { p.x / len, p.y / len }; +} + +static inline hb_cairo_point_t +_hb_cairo_sum (hb_cairo_point_t p, hb_cairo_point_t q) +{ + return hb_cairo_point_t { p.x + q.x, p.y + q.y }; +} + +static inline hb_cairo_point_t +_hb_cairo_difference (hb_cairo_point_t p, hb_cairo_point_t q) +{ + return hb_cairo_point_t { p.x - q.x, p.y - q.y }; +} + +static inline hb_cairo_point_t +_hb_cairo_scale (hb_cairo_point_t p, float f) +{ + return hb_cairo_point_t { p.x * f, p.y * f }; +} + +typedef struct { + hb_cairo_point_t center, p0, c0, c1, p1; + hb_cairo_color_t color0, color1; +} hb_cairo_patch_t; + +static void +_hb_cairo_add_patch (cairo_pattern_t *pattern, hb_cairo_point_t *center, hb_cairo_patch_t *p) +{ + cairo_mesh_pattern_begin_patch (pattern); + cairo_mesh_pattern_move_to (pattern, (double) center->x, (double) center->y); + cairo_mesh_pattern_line_to (pattern, (double) p->p0.x, (double) p->p0.y); + cairo_mesh_pattern_curve_to (pattern, + (double) p->c0.x, (double) p->c0.y, + (double) p->c1.x, (double) p->c1.y, + (double) p->p1.x, (double) p->p1.y); + cairo_mesh_pattern_line_to (pattern, (double) center->x, (double) center->y); + cairo_mesh_pattern_set_corner_color_rgba (pattern, 0, + (double) p->color0.r, + (double) p->color0.g, + (double) p->color0.b, + (double) p->color0.a); + cairo_mesh_pattern_set_corner_color_rgba (pattern, 1, + (double) p->color0.r, + (double) p->color0.g, + (double) p->color0.b, + (double) p->color0.a); + cairo_mesh_pattern_set_corner_color_rgba (pattern, 2, + (double) p->color1.r, + (double) p->color1.g, + (double) p->color1.b, + (double) p->color1.a); + cairo_mesh_pattern_set_corner_color_rgba (pattern, 3, + (double) p->color1.r, + (double) p->color1.g, + (double) p->color1.b, + (double) p->color1.a); + cairo_mesh_pattern_end_patch (pattern); +} + +#define MAX_ANGLE (HB_PI / 8.f) + +static void +_hb_cairo_add_sweep_gradient_patches1 (float cx, float cy, float radius, + float a0, hb_cairo_color_t *c0, + float a1, hb_cairo_color_t *c1, + cairo_pattern_t *pattern) +{ + hb_cairo_point_t center = hb_cairo_point_t { cx, cy }; + int num_splits; + hb_cairo_point_t p0; + hb_cairo_color_t color0, color1; + + num_splits = ceilf (fabsf (a1 - a0) / MAX_ANGLE); + p0 = hb_cairo_point_t { cosf (a0), sinf (a0) }; + color0 = *c0; + + for (int a = 0; a < num_splits; a++) + { + float k = (a + 1.) / num_splits; + float angle1; + hb_cairo_point_t p1; + hb_cairo_point_t A, U; + hb_cairo_point_t C0, C1; + hb_cairo_patch_t patch; + + angle1 = _hb_cairo_interpolate (a0, a1, k); + _hb_cairo_interpolate_colors (c0, c1, k, &color1); + + patch.color0 = color0; + patch.color1 = color1; + + p1 = hb_cairo_point_t { cosf (angle1), sinf (angle1) }; + patch.p0 = _hb_cairo_sum (center, _hb_cairo_scale (p0, radius)); + patch.p1 = _hb_cairo_sum (center, _hb_cairo_scale (p1, radius)); + + A = _hb_cairo_normalize (_hb_cairo_sum (p0, p1)); + U = hb_cairo_point_t { -A.y, A.x }; + C0 = _hb_cairo_sum (A, _hb_cairo_scale (U, _hb_cairo_dot (_hb_cairo_difference (p0, A), p0) / _hb_cairo_dot (U, p0))); + C1 = _hb_cairo_sum (A, _hb_cairo_scale (U, _hb_cairo_dot (_hb_cairo_difference (p1, A), p1) / _hb_cairo_dot (U, p1))); + + patch.c0 = _hb_cairo_sum (center, _hb_cairo_scale (_hb_cairo_sum (C0, _hb_cairo_scale (_hb_cairo_difference (C0, p0), 0.33333f)), radius)); + patch.c1 = _hb_cairo_sum (center, _hb_cairo_scale (_hb_cairo_sum (C1, _hb_cairo_scale (_hb_cairo_difference (C1, p1), 0.33333f)), radius)); + + _hb_cairo_add_patch (pattern, ¢er, &patch); + + p0 = p1; + color0 = color1; + } +} + +static void +_hb_cairo_add_sweep_gradient_patches (hb_color_stop_t *stops, + unsigned int n_stops, + cairo_extend_t extend, + float cx, float cy, + float radius, + float start_angle, + float end_angle, + cairo_pattern_t *pattern) +{ + float angles_[PREALLOCATED_COLOR_STOPS]; + float *angles = angles_; + hb_cairo_color_t colors_[PREALLOCATED_COLOR_STOPS]; + hb_cairo_color_t *colors = colors_; + hb_cairo_color_t color0, color1; + + if (start_angle == end_angle) + { + if (extend == CAIRO_EXTEND_PAD) + { + hb_cairo_color_t c; + if (start_angle > 0) + { + c.r = hb_color_get_red (stops[0].color) / 255.; + c.g = hb_color_get_green (stops[0].color) / 255.; + c.b = hb_color_get_blue (stops[0].color) / 255.; + c.a = hb_color_get_alpha (stops[0].color) / 255.; + _hb_cairo_add_sweep_gradient_patches1 (cx, cy, radius, + 0., &c, + start_angle, &c, + pattern); + } + if (end_angle < HB_2_PI) + { + c.r = hb_color_get_red (stops[n_stops - 1].color) / 255.; + c.g = hb_color_get_green (stops[n_stops - 1].color) / 255.; + c.b = hb_color_get_blue (stops[n_stops - 1].color) / 255.; + c.a = hb_color_get_alpha (stops[n_stops - 1].color) / 255.; + _hb_cairo_add_sweep_gradient_patches1 (cx, cy, radius, + end_angle, &c, + HB_2_PI, &c, + pattern); + } + } + return; + } + + assert (start_angle != end_angle); + + /* handle directions */ + if (end_angle < start_angle) + { + hb_swap (start_angle, end_angle); + + for (unsigned i = 0; i < n_stops - 1 - i; i++) + hb_swap (stops[i], stops[n_stops - 1 - i]); + for (unsigned i = 0; i < n_stops; i++) + stops[i].offset = 1 - stops[i].offset; + } + + if (n_stops > PREALLOCATED_COLOR_STOPS) + { + angles = (float *) hb_malloc (sizeof (float) * n_stops); + colors = (hb_cairo_color_t *) hb_malloc (sizeof (hb_cairo_color_t) * n_stops); + if (unlikely (!angles || !colors)) + { + hb_free (angles); + hb_free (colors); + return; + } + } + + for (unsigned i = 0; i < n_stops; i++) + { + angles[i] = start_angle + stops[i].offset * (end_angle - start_angle); + colors[i].r = hb_color_get_red (stops[i].color) / 255.; + colors[i].g = hb_color_get_green (stops[i].color) / 255.; + colors[i].b = hb_color_get_blue (stops[i].color) / 255.; + colors[i].a = hb_color_get_alpha (stops[i].color) / 255.; + } + + if (extend == CAIRO_EXTEND_PAD) + { + unsigned pos; + + color0 = colors[0]; + for (pos = 0; pos < n_stops; pos++) + { + if (angles[pos] >= 0) + { + if (pos > 0) + { + float k = (0 - angles[pos - 1]) / (angles[pos] - angles[pos - 1]); + _hb_cairo_interpolate_colors (&colors[pos-1], &colors[pos], k, &color0); + } + break; + } + } + if (pos == n_stops) + { + /* everything is below 0 */ + color0 = colors[n_stops-1]; + _hb_cairo_add_sweep_gradient_patches1 (cx, cy, radius, + 0., &color0, + HB_2_PI, &color0, + pattern); + goto done; + } + + _hb_cairo_add_sweep_gradient_patches1 (cx, cy, radius, + 0., &color0, + angles[pos], &colors[pos], + pattern); + + for (pos++; pos < n_stops; pos++) + { + if (angles[pos] <= HB_2_PI) + { + _hb_cairo_add_sweep_gradient_patches1 (cx, cy, radius, + angles[pos - 1], &colors[pos-1], + angles[pos], &colors[pos], + pattern); + } + else + { + float k = (HB_2_PI - angles[pos - 1]) / (angles[pos] - angles[pos - 1]); + _hb_cairo_interpolate_colors (&colors[pos - 1], &colors[pos], k, &color1); + _hb_cairo_add_sweep_gradient_patches1 (cx, cy, radius, + angles[pos - 1], &colors[pos - 1], + HB_2_PI, &color1, + pattern); + break; + } + } + + if (pos == n_stops) + { + /* everything is below 2*M_PI */ + color0 = colors[n_stops - 1]; + _hb_cairo_add_sweep_gradient_patches1 (cx, cy, radius, + angles[n_stops - 1], &color0, + HB_2_PI, &color0, + pattern); + goto done; + } + } + else + { + int k; + float span; + + span = angles[n_stops - 1] - angles[0]; + k = 0; + if (angles[0] >= 0) + { + float ss = angles[0]; + while (ss > 0) + { + if (span > 0) + { + ss -= span; + k--; + } + else + { + ss += span; + k++; + } + } + } + else if (angles[0] < 0) + { + float ee = angles[n_stops - 1]; + while (ee < 0) + { + if (span > 0) + { + ee += span; + k++; + } + else + { + ee -= span; + k--; + } + } + } + + //assert (angles[0] + k * span <= 0 && 0 < angles[n_stops - 1] + k * span); + span = fabsf (span); + + for (signed l = k; l < 1000; l++) + { + for (unsigned i = 1; i < n_stops; i++) + { + float a0, a1; + hb_cairo_color_t *c0, *c1; + + if ((l % 2 != 0) && (extend == CAIRO_EXTEND_REFLECT)) + { + a0 = angles[0] + angles[n_stops - 1] - angles[n_stops - 1 - (i-1)] + l * span; + a1 = angles[0] + angles[n_stops - 1] - angles[n_stops - 1 - i] + l * span; + c0 = &colors[n_stops - 1 - (i - 1)]; + c1 = &colors[n_stops - 1 - i]; + } + else + { + a0 = angles[i-1] + l * span; + a1 = angles[i] + l * span; + c0 = &colors[i-1]; + c1 = &colors[i]; + } + + if (a1 < 0) + continue; + if (a0 < 0) + { + hb_cairo_color_t color; + float f = (0 - a0)/(a1 - a0); + _hb_cairo_interpolate_colors (c0, c1, f, &color); + _hb_cairo_add_sweep_gradient_patches1 (cx, cy, radius, + 0, &color, + a1, c1, + pattern); + } + else if (a1 >= HB_2_PI) + { + hb_cairo_color_t color; + float f = (HB_2_PI - a0)/(a1 - a0); + _hb_cairo_interpolate_colors (c0, c1, f, &color); + _hb_cairo_add_sweep_gradient_patches1 (cx, cy, radius, + a0, c0, + HB_2_PI, &color, + pattern); + goto done; + } + else + { + _hb_cairo_add_sweep_gradient_patches1 (cx, cy, radius, + a0, c0, + a1, c1, + pattern); + } + } + } + } + +done: + + if (angles != angles_) + hb_free (angles); + if (colors != colors_) + hb_free (colors); +} + +void +_hb_cairo_paint_sweep_gradient (hb_cairo_context_t *c, + hb_color_line_t *color_line, + float cx, float cy, + float start_angle, + float end_angle) +{ + cairo_t *cr = c->cr; + + unsigned int len = PREALLOCATED_COLOR_STOPS; + hb_color_stop_t stops_[PREALLOCATED_COLOR_STOPS]; + hb_color_stop_t *stops = stops_; + cairo_extend_t extend; + double x1, y1, x2, y2; + float max_x, max_y, radius; + cairo_pattern_t *pattern; + + if (unlikely (!_hb_cairo_get_color_stops (c, color_line, &len, &stops))) + return; + + hb_qsort (stops, len, sizeof (hb_color_stop_t), _hb_cairo_cmp_color_stop); + + cairo_clip_extents (cr, &x1, &y1, &x2, &y2); + max_x = (float) hb_max ((x1 - (double) cx) * (x1 - (double) cx), (x2 - (double) cx) * (x2 - (double) cx)); + max_y = (float) hb_max ((y1 - (double) cy) * (y1 - (double) cy), (y2 - (double) cy) * (y2 - (double) cy)); + radius = sqrtf (max_x + max_y); + + extend = hb_cairo_extend (hb_color_line_get_extend (color_line)); + pattern = cairo_pattern_create_mesh (); + + _hb_cairo_add_sweep_gradient_patches (stops, len, extend, cx, cy, + radius, start_angle, end_angle, pattern); + + cairo_set_source (cr, pattern); + cairo_paint (cr); + + cairo_pattern_destroy (pattern); + + if (stops != stops_) + hb_free (stops); +} + +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-cairo-utils.hh b/src/3rdparty/harfbuzz-ng/src/hb-cairo-utils.hh new file mode 100644 index 00000000000..a26bf59d918 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-cairo-utils.hh @@ -0,0 +1,107 @@ +/* + * Copyright © 2022 Red Hat, 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): Matthias Clasen + */ + +#ifndef HB_CAIRO_UTILS_H +#define HB_CAIRO_UTILS_H + +#include "hb.hh" +#include "hb-cairo.h" + + +typedef struct +{ + cairo_scaled_font_t *scaled_font; + cairo_t *cr; + hb_map_t *color_cache; +} hb_cairo_context_t; + +static inline cairo_operator_t +_hb_paint_composite_mode_to_cairo (hb_paint_composite_mode_t mode) +{ + switch (mode) + { + case HB_PAINT_COMPOSITE_MODE_CLEAR: return CAIRO_OPERATOR_CLEAR; + case HB_PAINT_COMPOSITE_MODE_SRC: return CAIRO_OPERATOR_SOURCE; + case HB_PAINT_COMPOSITE_MODE_DEST: return CAIRO_OPERATOR_DEST; + case HB_PAINT_COMPOSITE_MODE_SRC_OVER: return CAIRO_OPERATOR_OVER; + case HB_PAINT_COMPOSITE_MODE_DEST_OVER: return CAIRO_OPERATOR_DEST_OVER; + case HB_PAINT_COMPOSITE_MODE_SRC_IN: return CAIRO_OPERATOR_IN; + case HB_PAINT_COMPOSITE_MODE_DEST_IN: return CAIRO_OPERATOR_DEST_IN; + case HB_PAINT_COMPOSITE_MODE_SRC_OUT: return CAIRO_OPERATOR_OUT; + case HB_PAINT_COMPOSITE_MODE_DEST_OUT: return CAIRO_OPERATOR_DEST_OUT; + case HB_PAINT_COMPOSITE_MODE_SRC_ATOP: return CAIRO_OPERATOR_ATOP; + case HB_PAINT_COMPOSITE_MODE_DEST_ATOP: return CAIRO_OPERATOR_DEST_ATOP; + case HB_PAINT_COMPOSITE_MODE_XOR: return CAIRO_OPERATOR_XOR; + case HB_PAINT_COMPOSITE_MODE_PLUS: return CAIRO_OPERATOR_ADD; + case HB_PAINT_COMPOSITE_MODE_SCREEN: return CAIRO_OPERATOR_SCREEN; + case HB_PAINT_COMPOSITE_MODE_OVERLAY: return CAIRO_OPERATOR_OVERLAY; + case HB_PAINT_COMPOSITE_MODE_DARKEN: return CAIRO_OPERATOR_DARKEN; + case HB_PAINT_COMPOSITE_MODE_LIGHTEN: return CAIRO_OPERATOR_LIGHTEN; + case HB_PAINT_COMPOSITE_MODE_COLOR_DODGE: return CAIRO_OPERATOR_COLOR_DODGE; + case HB_PAINT_COMPOSITE_MODE_COLOR_BURN: return CAIRO_OPERATOR_COLOR_BURN; + case HB_PAINT_COMPOSITE_MODE_HARD_LIGHT: return CAIRO_OPERATOR_HARD_LIGHT; + case HB_PAINT_COMPOSITE_MODE_SOFT_LIGHT: return CAIRO_OPERATOR_SOFT_LIGHT; + case HB_PAINT_COMPOSITE_MODE_DIFFERENCE: return CAIRO_OPERATOR_DIFFERENCE; + case HB_PAINT_COMPOSITE_MODE_EXCLUSION: return CAIRO_OPERATOR_EXCLUSION; + case HB_PAINT_COMPOSITE_MODE_MULTIPLY: return CAIRO_OPERATOR_MULTIPLY; + case HB_PAINT_COMPOSITE_MODE_HSL_HUE: return CAIRO_OPERATOR_HSL_HUE; + case HB_PAINT_COMPOSITE_MODE_HSL_SATURATION: return CAIRO_OPERATOR_HSL_SATURATION; + case HB_PAINT_COMPOSITE_MODE_HSL_COLOR: return CAIRO_OPERATOR_HSL_COLOR; + case HB_PAINT_COMPOSITE_MODE_HSL_LUMINOSITY: return CAIRO_OPERATOR_HSL_LUMINOSITY; + default: return CAIRO_OPERATOR_CLEAR; + } +} + +HB_INTERNAL hb_bool_t +_hb_cairo_paint_glyph_image (hb_cairo_context_t *c, + hb_blob_t *blob, + unsigned width, + unsigned height, + hb_tag_t format, + float slant, + hb_glyph_extents_t *extents); + +HB_INTERNAL void +_hb_cairo_paint_linear_gradient (hb_cairo_context_t *c, + hb_color_line_t *color_line, + float x0, float y0, + float x1, float y1, + float x2, float y2); + +HB_INTERNAL void +_hb_cairo_paint_radial_gradient (hb_cairo_context_t *c, + hb_color_line_t *color_line, + float x0, float y0, float r0, + float x1, float y1, float r1); + +HB_INTERNAL void +_hb_cairo_paint_sweep_gradient (hb_cairo_context_t *c, + hb_color_line_t *color_line, + float x0, float y0, + float start_angle, float end_angle); + + +#endif /* HB_CAIRO_UTILS_H */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-cairo.cc b/src/3rdparty/harfbuzz-ng/src/hb-cairo.cc new file mode 100644 index 00000000000..f4f9f54ab3b --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-cairo.cc @@ -0,0 +1,1037 @@ +/* + * Copyright © 2022 Red Hat, 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. + * + * Red Hat Author(s): Matthias Clasen + */ + +#include "hb.hh" + +#ifdef HAVE_CAIRO + +#include "hb-cairo.h" + +#include "hb-cairo-utils.hh" + +#include "hb-machinery.hh" +#include "hb-utf.hh" + + +/** + * SECTION:hb-cairo + * @title: hb-cairo + * @short_description: Cairo integration + * @include: hb-cairo.h + * + * Functions for using HarfBuzz with the cairo library. + * + * HarfBuzz supports using cairo for rendering. + **/ + +static void +hb_cairo_move_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *draw_data, + hb_draw_state_t *st HB_UNUSED, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + cairo_t *cr = (cairo_t *) draw_data; + + cairo_move_to (cr, (double) to_x, (double) to_y); +} + +static void +hb_cairo_line_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *draw_data, + hb_draw_state_t *st HB_UNUSED, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + cairo_t *cr = (cairo_t *) draw_data; + + cairo_line_to (cr, (double) to_x, (double) to_y); +} + +static void +hb_cairo_cubic_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *draw_data, + hb_draw_state_t *st HB_UNUSED, + float control1_x, float control1_y, + float control2_x, float control2_y, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + cairo_t *cr = (cairo_t *) draw_data; + + cairo_curve_to (cr, + (double) control1_x, (double) control1_y, + (double) control2_x, (double) control2_y, + (double) to_x, (double) to_y); +} + +static void +hb_cairo_close_path (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *draw_data, + hb_draw_state_t *st HB_UNUSED, + void *user_data HB_UNUSED) +{ + cairo_t *cr = (cairo_t *) draw_data; + + cairo_close_path (cr); +} + +static inline void free_static_cairo_draw_funcs (); + +static struct hb_cairo_draw_funcs_lazy_loader_t : hb_draw_funcs_lazy_loader_t +{ + static hb_draw_funcs_t *create () + { + hb_draw_funcs_t *funcs = hb_draw_funcs_create (); + + hb_draw_funcs_set_move_to_func (funcs, hb_cairo_move_to, nullptr, nullptr); + hb_draw_funcs_set_line_to_func (funcs, hb_cairo_line_to, nullptr, nullptr); + hb_draw_funcs_set_cubic_to_func (funcs, hb_cairo_cubic_to, nullptr, nullptr); + hb_draw_funcs_set_close_path_func (funcs, hb_cairo_close_path, nullptr, nullptr); + + hb_draw_funcs_make_immutable (funcs); + + hb_atexit (free_static_cairo_draw_funcs); + + return funcs; + } +} static_cairo_draw_funcs; + +static inline +void free_static_cairo_draw_funcs () +{ + static_cairo_draw_funcs.free_instance (); +} + +static hb_draw_funcs_t * +hb_cairo_draw_get_funcs () +{ + return static_cairo_draw_funcs.get_unconst (); +} + + +#ifdef HAVE_CAIRO_USER_FONT_FACE_SET_RENDER_COLOR_GLYPH_FUNC + +static void +hb_cairo_push_transform (hb_paint_funcs_t *pfuncs HB_UNUSED, + void *paint_data, + float xx, float yx, + float xy, float yy, + float dx, float dy, + void *user_data HB_UNUSED) +{ + hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data; + cairo_t *cr = c->cr; + + cairo_matrix_t m; + + cairo_save (cr); + cairo_matrix_init (&m, (double) xx, (double) yx, + (double) xy, (double) yy, + (double) dx, (double) dy); + cairo_transform (cr, &m); +} + +static void +hb_cairo_pop_transform (hb_paint_funcs_t *pfuncs HB_UNUSED, + void *paint_data, + void *user_data HB_UNUSED) +{ + hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data; + cairo_t *cr = c->cr; + + cairo_restore (cr); +} + +static hb_bool_t +hb_cairo_paint_color_glyph (hb_paint_funcs_t *pfuncs HB_UNUSED, + void *paint_data, + hb_codepoint_t glyph, + hb_font_t *font, + void *user_data HB_UNUSED) +{ + hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data; + cairo_t *cr = c->cr; + + cairo_save (cr); + + hb_position_t x_scale, y_scale; + hb_font_get_scale (font, &x_scale, &y_scale); + cairo_scale (cr, x_scale, y_scale); + + cairo_glyph_t cairo_glyph = { glyph, 0, 0 }; + cairo_set_scaled_font (cr, c->scaled_font); + cairo_set_font_size (cr, 1); + cairo_show_glyphs (cr, &cairo_glyph, 1); + + cairo_restore (cr); + + return true; +} + +static void +hb_cairo_push_clip_glyph (hb_paint_funcs_t *pfuncs HB_UNUSED, + void *paint_data, + hb_codepoint_t glyph, + hb_font_t *font, + void *user_data HB_UNUSED) +{ + hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data; + cairo_t *cr = c->cr; + + cairo_save (cr); + cairo_new_path (cr); + hb_font_draw_glyph (font, glyph, hb_cairo_draw_get_funcs (), cr); + cairo_close_path (cr); + cairo_clip (cr); +} + +static void +hb_cairo_push_clip_rectangle (hb_paint_funcs_t *pfuncs HB_UNUSED, + void *paint_data, + float xmin, float ymin, float xmax, float ymax, + void *user_data HB_UNUSED) +{ + hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data; + cairo_t *cr = c->cr; + + cairo_save (cr); + cairo_rectangle (cr, + (double) xmin, (double) ymin, + (double) (xmax - xmin), (double) (ymax - ymin)); + cairo_clip (cr); +} + +static void +hb_cairo_pop_clip (hb_paint_funcs_t *pfuncs HB_UNUSED, + void *paint_data, + void *user_data HB_UNUSED) +{ + hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data; + cairo_t *cr = c->cr; + + cairo_restore (cr); +} + +static void +hb_cairo_push_group (hb_paint_funcs_t *pfuncs HB_UNUSED, + void *paint_data, + void *user_data HB_UNUSED) +{ + hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data; + cairo_t *cr = c->cr; + + cairo_save (cr); + cairo_push_group (cr); +} + +static void +hb_cairo_pop_group (hb_paint_funcs_t *pfuncs HB_UNUSED, + void *paint_data, + hb_paint_composite_mode_t mode, + void *user_data HB_UNUSED) +{ + hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data; + cairo_t *cr = c->cr; + + cairo_pop_group_to_source (cr); + cairo_set_operator (cr, _hb_paint_composite_mode_to_cairo (mode)); + cairo_paint (cr); + + cairo_restore (cr); +} + +static void +hb_cairo_paint_color (hb_paint_funcs_t *pfuncs HB_UNUSED, + void *paint_data, + hb_bool_t use_foreground, + hb_color_t color, + void *user_data HB_UNUSED) +{ + hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data; + cairo_t *cr = c->cr; + + if (use_foreground) + { +#ifdef HAVE_CAIRO_USER_SCALED_FONT_GET_FOREGROUND_SOURCE + double r, g, b, a; + cairo_pattern_t *foreground = cairo_user_scaled_font_get_foreground_source (c->scaled_font); + if (cairo_pattern_get_rgba (foreground, &r, &g, &b, &a) == CAIRO_STATUS_SUCCESS) + cairo_set_source_rgba (cr, r, g, b, a * hb_color_get_alpha (color) / 255.); + else +#endif + cairo_set_source_rgba (cr, 0, 0, 0, hb_color_get_alpha (color) / 255.); + } + else + cairo_set_source_rgba (cr, + hb_color_get_red (color) / 255., + hb_color_get_green (color) / 255., + hb_color_get_blue (color) / 255., + hb_color_get_alpha (color) / 255.); + cairo_paint (cr); +} + +static hb_bool_t +hb_cairo_paint_image (hb_paint_funcs_t *pfuncs HB_UNUSED, + void *paint_data, + hb_blob_t *blob, + unsigned width, + unsigned height, + hb_tag_t format, + float slant, + hb_glyph_extents_t *extents, + void *user_data HB_UNUSED) +{ + hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data; + + return _hb_cairo_paint_glyph_image (c, blob, width, height, format, slant, extents); +} + +static void +hb_cairo_paint_linear_gradient (hb_paint_funcs_t *pfuncs HB_UNUSED, + void *paint_data, + hb_color_line_t *color_line, + float x0, float y0, + float x1, float y1, + float x2, float y2, + void *user_data HB_UNUSED) +{ + hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data; + + _hb_cairo_paint_linear_gradient (c, color_line, x0, y0, x1, y1, x2, y2); +} + +static void +hb_cairo_paint_radial_gradient (hb_paint_funcs_t *pfuncs HB_UNUSED, + void *paint_data, + hb_color_line_t *color_line, + float x0, float y0, float r0, + float x1, float y1, float r1, + void *user_data HB_UNUSED) +{ + hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data; + + _hb_cairo_paint_radial_gradient (c, color_line, x0, y0, r0, x1, y1, r1); +} + +static void +hb_cairo_paint_sweep_gradient (hb_paint_funcs_t *pfuncs HB_UNUSED, + void *paint_data, + hb_color_line_t *color_line, + float x0, float y0, + float start_angle, float end_angle, + void *user_data HB_UNUSED) +{ + hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data; + + _hb_cairo_paint_sweep_gradient (c, color_line, x0, y0, start_angle, end_angle); +} + +static const cairo_user_data_key_t color_cache_key = {0}; + +static void +_hb_cairo_destroy_map (void *p) +{ + hb_map_destroy ((hb_map_t *) p); +} + +static hb_bool_t +hb_cairo_paint_custom_palette_color (hb_paint_funcs_t *funcs, + void *paint_data, + unsigned int color_index, + hb_color_t *color, + void *user_data HB_UNUSED) +{ +#ifdef HAVE_CAIRO_FONT_OPTIONS_GET_CUSTOM_PALETTE_COLOR + hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data; + cairo_t *cr = c->cr; + +#define HB_DEADBEEF HB_TAG(0xDE,0xAD,0xBE,0xEF) + + hb_map_t *color_cache = c->color_cache; + hb_codepoint_t *v; + if (likely (color_cache && color_cache->has (color_index, &v))) + { + if (*v == HB_DEADBEEF) + return false; + *color = *v; + return true; + } + + cairo_font_options_t *options; + double red, green, blue, alpha; + + options = cairo_font_options_create (); + cairo_get_font_options (cr, options); + if (CAIRO_STATUS_SUCCESS == + cairo_font_options_get_custom_palette_color (options, color_index, + &red, &green, &blue, &alpha)) + { + cairo_font_options_destroy (options); + *color = HB_COLOR (round (255 * blue), + round (255 * green), + round (255 * red), + round (255 * alpha)); + + if (likely (color_cache && *color != HB_DEADBEEF)) + color_cache->set (color_index, *color); + + return true; + } + cairo_font_options_destroy (options); + + if (likely (color_cache)) + color_cache->set (color_index, HB_DEADBEEF); + +#undef HB_DEADBEEF + +#endif + + return false; +} + +static inline void free_static_cairo_paint_funcs (); + +static struct hb_cairo_paint_funcs_lazy_loader_t : hb_paint_funcs_lazy_loader_t +{ + static hb_paint_funcs_t *create () + { + hb_paint_funcs_t *funcs = hb_paint_funcs_create (); + + hb_paint_funcs_set_push_transform_func (funcs, hb_cairo_push_transform, nullptr, nullptr); + hb_paint_funcs_set_pop_transform_func (funcs, hb_cairo_pop_transform, nullptr, nullptr); + hb_paint_funcs_set_color_glyph_func (funcs, hb_cairo_paint_color_glyph, nullptr, nullptr); + hb_paint_funcs_set_push_clip_glyph_func (funcs, hb_cairo_push_clip_glyph, nullptr, nullptr); + hb_paint_funcs_set_push_clip_rectangle_func (funcs, hb_cairo_push_clip_rectangle, nullptr, nullptr); + hb_paint_funcs_set_pop_clip_func (funcs, hb_cairo_pop_clip, nullptr, nullptr); + hb_paint_funcs_set_push_group_func (funcs, hb_cairo_push_group, nullptr, nullptr); + hb_paint_funcs_set_pop_group_func (funcs, hb_cairo_pop_group, nullptr, nullptr); + hb_paint_funcs_set_color_func (funcs, hb_cairo_paint_color, nullptr, nullptr); + hb_paint_funcs_set_image_func (funcs, hb_cairo_paint_image, nullptr, nullptr); + hb_paint_funcs_set_linear_gradient_func (funcs, hb_cairo_paint_linear_gradient, nullptr, nullptr); + hb_paint_funcs_set_radial_gradient_func (funcs, hb_cairo_paint_radial_gradient, nullptr, nullptr); + hb_paint_funcs_set_sweep_gradient_func (funcs, hb_cairo_paint_sweep_gradient, nullptr, nullptr); + hb_paint_funcs_set_custom_palette_color_func (funcs, hb_cairo_paint_custom_palette_color, nullptr, nullptr); + + hb_paint_funcs_make_immutable (funcs); + + hb_atexit (free_static_cairo_paint_funcs); + + return funcs; + } +} static_cairo_paint_funcs; + +static inline +void free_static_cairo_paint_funcs () +{ + static_cairo_paint_funcs.free_instance (); +} + +static hb_paint_funcs_t * +hb_cairo_paint_get_funcs () +{ + return static_cairo_paint_funcs.get_unconst (); +} +#endif + +static const cairo_user_data_key_t hb_cairo_face_user_data_key = {0}; +static const cairo_user_data_key_t hb_cairo_font_user_data_key = {0}; +static const cairo_user_data_key_t hb_cairo_font_init_func_user_data_key = {0}; +static const cairo_user_data_key_t hb_cairo_font_init_user_data_user_data_key = {0}; +static const cairo_user_data_key_t hb_cairo_scale_factor_user_data_key = {0}; + +static void hb_cairo_face_destroy (void *p) { hb_face_destroy ((hb_face_t *) p); } +static void hb_cairo_font_destroy (void *p) { hb_font_destroy ((hb_font_t *) p); } + +static cairo_status_t +hb_cairo_init_scaled_font (cairo_scaled_font_t *scaled_font, + cairo_t *cr HB_UNUSED, + cairo_font_extents_t *extents) +{ + cairo_font_face_t *font_face = cairo_scaled_font_get_font_face (scaled_font); + + hb_font_t *font = (hb_font_t *) cairo_font_face_get_user_data (font_face, + &hb_cairo_font_user_data_key); + + if (!font) + { + hb_face_t *face = (hb_face_t *) cairo_font_face_get_user_data (font_face, + &hb_cairo_face_user_data_key); + font = hb_font_create (face); + +#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1,16,0) + cairo_font_options_t *font_options = cairo_font_options_create (); + + // Set variations + cairo_scaled_font_get_font_options (scaled_font, font_options); + const char *variations = cairo_font_options_get_variations (font_options); + hb_vector_t vars; + const char *p = variations; + while (p && *p) + { + const char *end = strpbrk ((char *) p, ", "); + hb_variation_t var; + if (hb_variation_from_string (p, end ? end - p : -1, &var)) + vars.push (var); + p = end ? end + 1 : nullptr; + } + hb_font_set_variations (font, &vars[0], vars.length); + + cairo_font_options_destroy (font_options); +#endif + + // Set scale; Note: should NOT set slant, or we'll double-slant. + unsigned scale_factor = hb_cairo_font_face_get_scale_factor (font_face); + if (scale_factor) + { + cairo_matrix_t font_matrix; + cairo_scaled_font_get_scale_matrix (scaled_font, &font_matrix); + hb_font_set_scale (font, + round (font_matrix.xx * scale_factor), + round (font_matrix.yy * scale_factor)); + } + + auto *init_func = (hb_cairo_font_init_func_t) + cairo_font_face_get_user_data (font_face, + &hb_cairo_font_init_func_user_data_key); + if (init_func) + { + void *user_data = cairo_font_face_get_user_data (font_face, + &hb_cairo_font_init_user_data_user_data_key); + font = init_func (font, scaled_font, user_data); + } + + hb_font_make_immutable (font); + } + + cairo_scaled_font_set_user_data (scaled_font, + &hb_cairo_font_user_data_key, + (void *) hb_font_reference (font), + hb_cairo_font_destroy); + + hb_position_t x_scale, y_scale; + hb_font_get_scale (font, &x_scale, &y_scale); + + hb_font_extents_t hb_extents; + hb_font_get_h_extents (font, &hb_extents); + + extents->ascent = (double) hb_extents.ascender / y_scale; + extents->descent = (double) -hb_extents.descender / y_scale; + extents->height = extents->ascent + extents->descent; + +#ifdef HAVE_CAIRO_USER_FONT_FACE_SET_RENDER_COLOR_GLYPH_FUNC + hb_map_t *color_cache = hb_map_create (); + if (unlikely (CAIRO_STATUS_SUCCESS != cairo_scaled_font_set_user_data (scaled_font, + &color_cache_key, + color_cache, + _hb_cairo_destroy_map))) + hb_map_destroy (color_cache); +#endif + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +hb_cairo_text_to_glyphs (cairo_scaled_font_t *scaled_font, + const char *utf8, + int utf8_len, + cairo_glyph_t **glyphs, + int *num_glyphs, + cairo_text_cluster_t **clusters, + int *num_clusters, + cairo_text_cluster_flags_t *cluster_flags) +{ + hb_font_t *font = (hb_font_t *) cairo_scaled_font_get_user_data (scaled_font, + &hb_cairo_font_user_data_key); + + hb_buffer_t *buffer = hb_buffer_create (); + hb_buffer_add_utf8 (buffer, utf8, utf8_len, 0, utf8_len); + hb_buffer_guess_segment_properties (buffer); + hb_shape (font, buffer, nullptr, 0); + + hb_cairo_glyphs_from_buffer (buffer, + true, + font->x_scale, font->y_scale, + 0., 0., + utf8, utf8_len, + glyphs, (unsigned *) num_glyphs, + clusters, (unsigned *) num_clusters, + cluster_flags); + + hb_buffer_destroy (buffer); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +hb_cairo_render_glyph (cairo_scaled_font_t *scaled_font, + unsigned long glyph, + cairo_t *cr, + cairo_text_extents_t *extents) +{ + hb_font_t *font = (hb_font_t *) cairo_scaled_font_get_user_data (scaled_font, + &hb_cairo_font_user_data_key); + + hb_position_t x_scale, y_scale; + hb_font_get_scale (font, &x_scale, &y_scale); + cairo_scale (cr, +1./x_scale, -1./y_scale); + + hb_font_draw_glyph (font, glyph, hb_cairo_draw_get_funcs (), cr); + + cairo_fill (cr); + + return CAIRO_STATUS_SUCCESS; +} + +#ifdef HAVE_CAIRO_USER_FONT_FACE_SET_RENDER_COLOR_GLYPH_FUNC + +static cairo_status_t +hb_cairo_render_color_glyph (cairo_scaled_font_t *scaled_font, + unsigned long glyph, + cairo_t *cr, + cairo_text_extents_t *extents) +{ + hb_font_t *font = (hb_font_t *) cairo_scaled_font_get_user_data (scaled_font, + &hb_cairo_font_user_data_key); + + unsigned int palette = 0; +#ifdef CAIRO_COLOR_PALETTE_DEFAULT + cairo_font_options_t *options = cairo_font_options_create (); + cairo_scaled_font_get_font_options (scaled_font, options); + palette = cairo_font_options_get_color_palette (options); + cairo_font_options_destroy (options); +#endif + + hb_color_t color = HB_COLOR (0, 0, 0, 255); + hb_position_t x_scale, y_scale; + hb_font_get_scale (font, &x_scale, &y_scale); + cairo_scale (cr, +1./x_scale, -1./y_scale); + + hb_cairo_context_t c; + c.scaled_font = scaled_font; + c.cr = cr; + c.color_cache = (hb_map_t *) cairo_scaled_font_get_user_data (scaled_font, &color_cache_key); + + hb_font_paint_glyph (font, glyph, hb_cairo_paint_get_funcs (), &c, palette, color); + + + return CAIRO_STATUS_SUCCESS; +} + +#endif + +static cairo_font_face_t * +user_font_face_create (hb_face_t *face) +{ + cairo_font_face_t *cairo_face; + + cairo_face = cairo_user_font_face_create (); + cairo_user_font_face_set_init_func (cairo_face, hb_cairo_init_scaled_font); + cairo_user_font_face_set_text_to_glyphs_func (cairo_face, hb_cairo_text_to_glyphs); + cairo_user_font_face_set_render_glyph_func (cairo_face, hb_cairo_render_glyph); +#ifdef HAVE_CAIRO_USER_FONT_FACE_SET_RENDER_COLOR_GLYPH_FUNC + if (hb_ot_color_has_png (face) || hb_ot_color_has_layers (face) || hb_ot_color_has_paint (face)) + cairo_user_font_face_set_render_color_glyph_func (cairo_face, hb_cairo_render_color_glyph); +#endif + + if (unlikely (CAIRO_STATUS_SUCCESS != cairo_font_face_set_user_data (cairo_face, + &hb_cairo_face_user_data_key, + (void *) hb_face_reference (face), + hb_cairo_face_destroy))) + hb_face_destroy (face); + + return cairo_face; +} + +/** + * hb_cairo_font_face_create_for_font: + * @font: a #hb_font_t + * + * Creates a #cairo_font_face_t for rendering text according + * to @font. + * + * Note that the scale of @font does not affect the rendering, + * but the variations and slant that are set on @font do. + * + * Returns: (transfer full): a newly created #cairo_font_face_t + * + * Since: 7.0.0 + */ +cairo_font_face_t * +hb_cairo_font_face_create_for_font (hb_font_t *font) +{ + hb_font_make_immutable (font); + + auto *cairo_face = user_font_face_create (font->face); + + if (unlikely (CAIRO_STATUS_SUCCESS != cairo_font_face_set_user_data (cairo_face, + &hb_cairo_font_user_data_key, + (void *) hb_font_reference (font), + hb_cairo_font_destroy))) + hb_font_destroy (font); + + return cairo_face; +} + +/** + * hb_cairo_font_face_get_font: + * @font_face: a #cairo_font_face_t + * + * Gets the #hb_font_t that @font_face was created from. + * + * Returns: (nullable) (transfer none): the #hb_font_t that @font_face was created from + * + * Since: 7.0.0 + */ +hb_font_t * +hb_cairo_font_face_get_font (cairo_font_face_t *font_face) +{ + return (hb_font_t *) cairo_font_face_get_user_data (font_face, + &hb_cairo_font_user_data_key); +} + +/** + * hb_cairo_font_face_create_for_face: + * @face: a #hb_face_t + * + * Creates a #cairo_font_face_t for rendering text according + * to @face. + * + * Returns: (transfer full): a newly created #cairo_font_face_t + * + * Since: 7.0.0 + */ +cairo_font_face_t * +hb_cairo_font_face_create_for_face (hb_face_t *face) +{ + hb_face_make_immutable (face); + + return user_font_face_create (face); +} + +/** + * hb_cairo_font_face_get_face: + * @font_face: a #cairo_font_face_t + * + * Gets the #hb_face_t associated with @font_face. + * + * Returns: (nullable) (transfer none): the #hb_face_t associated with @font_face + * + * Since: 7.0.0 + */ +hb_face_t * +hb_cairo_font_face_get_face (cairo_font_face_t *font_face) +{ + return (hb_face_t *) cairo_font_face_get_user_data (font_face, + &hb_cairo_face_user_data_key); +} + +/** + * hb_cairo_font_face_set_font_init_func: + * @font_face: a #cairo_font_face_t + * @func: The virtual method to use + * @user_data: user data accompanying the method + * @destroy: function to call when @user_data is not needed anymore + * + * Set the virtual method to be called when a cairo + * face created using hb_cairo_font_face_create_for_face() + * creates an #hb_font_t for a #cairo_scaled_font_t. + * + * Since: 7.0.0 + */ +void +hb_cairo_font_face_set_font_init_func (cairo_font_face_t *font_face, + hb_cairo_font_init_func_t func, + void *user_data, + hb_destroy_func_t destroy) +{ + cairo_font_face_set_user_data (font_face, + &hb_cairo_font_init_func_user_data_key, + (void *) func, + nullptr); + if (unlikely (CAIRO_STATUS_SUCCESS != cairo_font_face_set_user_data (font_face, + &hb_cairo_font_init_user_data_user_data_key, + (void *) user_data, + destroy)) && destroy) + { + destroy (user_data); + cairo_font_face_set_user_data (font_face, + &hb_cairo_font_init_func_user_data_key, + nullptr, + nullptr); + } +} + +/** + * hb_cairo_scaled_font_get_font: + * @scaled_font: a #cairo_scaled_font_t + * + * Gets the #hb_font_t associated with @scaled_font. + * + * Returns: (nullable) (transfer none): the #hb_font_t associated with @scaled_font + * + * Since: 7.0.0 + */ +hb_font_t * +hb_cairo_scaled_font_get_font (cairo_scaled_font_t *scaled_font) +{ + return (hb_font_t *) cairo_scaled_font_get_user_data (scaled_font, &hb_cairo_font_user_data_key); +} + + +/** + * hb_cairo_font_face_set_scale_factor: + * @scale_factor: The scale factor to use. See below + * @font_face: a #cairo_font_face_t + * + * Sets the scale factor of the @font_face. Default scale + * factor is zero. + * + * When a #cairo_font_face_t is created from a #hb_face_t using + * hb_cairo_font_face_create_for_face(), such face will create + * #hb_font_t objects during scaled-font creation. The scale + * factor defines how the scale set on such #hb_font_t objects + * relates to the font-matrix (as such font size) of the cairo + * scaled-font. + * + * If the scale-factor is zero (default), then the scale of the + * #hb_font_t object will be left at default, which is the UPEM + * value of the respective #hb_face_t. + * + * If the scale-factor is set to non-zero, then the X and Y scale + * of the #hb_font_t object will be respectively set to the + * @scale_factor times the xx and yy elements of the scale-matrix + * of the cairo scaled-font being created. + * + * When using the hb_cairo_glyphs_from_buffer() API to convert the + * HarfBuzz glyph buffer that resulted from shaping with such a #hb_font_t, + * if the scale-factor was non-zero, you can pass it directly to + * that API as both X and Y scale factors. + * + * If the scale-factor was zero however, or the cairo face was + * created using the alternative constructor + * hb_cairo_font_face_create_for_font(), you need to calculate the + * correct X/Y scale-factors to pass to hb_cairo_glyphs_from_buffer() + * by dividing the #hb_font_t X/Y scale-factors by the + * cairo scaled-font's scale-matrix XX/YY components respectively + * and use those values. Or if you know that relationship offhand + * (because you set the scale of the #hb_font_t yourself), use + * the conversion rate involved. + * + * Since: 7.0.0 + */ +void +hb_cairo_font_face_set_scale_factor (cairo_font_face_t *font_face, + unsigned int scale_factor) +{ + cairo_font_face_set_user_data (font_face, + &hb_cairo_scale_factor_user_data_key, + (void *) (uintptr_t) scale_factor, + nullptr); +} + +/** + * hb_cairo_font_face_get_scale_factor: + * @font_face: a #cairo_font_face_t + * + * Gets the scale factor set on the @font_face. Defaults to zero. + * See hb_cairo_font_face_set_scale_factor() for details. + * + * Returns: the scale factor of @font_face + * + * Since: 7.0.0 + */ +unsigned int +hb_cairo_font_face_get_scale_factor (cairo_font_face_t *font_face) +{ + return (unsigned int) (uintptr_t) + cairo_font_face_get_user_data (font_face, + &hb_cairo_scale_factor_user_data_key); +} + + +/** + * hb_cairo_glyphs_from_buffer: + * @buffer: a #hb_buffer_t containing glyphs + * @utf8_clusters: `true` if @buffer clusters are in bytes, instead of characters + * @x_scale_factor: scale factor to divide #hb_position_t Y values by + * @y_scale_factor: scale factor to divide #hb_position_t X values by + * @x: X position to place first glyph + * @y: Y position to place first glyph + * @utf8: (nullable): the text that was shaped in @buffer + * @utf8_len: the length of @utf8 in bytes + * @glyphs: (out): return location for an array of #cairo_glyph_t + * @num_glyphs: (inout): return location for the length of @glyphs + * @clusters: (out) (nullable): return location for an array of cluster positions + * @num_clusters: (inout) (nullable): return location for the length of @clusters + * @cluster_flags: (out) (nullable): return location for cluster flags + * + * Extracts information from @buffer in a form that can be + * passed to cairo_show_text_glyphs() or cairo_show_glyphs(). + * This API is modeled after cairo_scaled_font_text_to_glyphs() and + * cairo_user_scaled_font_text_to_glyphs_func_t. + * + * The @num_glyphs argument should be preset to the number of glyph entries available + * in the @glyphs buffer. If the @glyphs buffer is `NULL`, the value of + * @num_glyphs must be zero. If the provided glyph array is too short for + * the conversion (or for convenience), a new glyph array may be allocated + * using cairo_glyph_allocate() and placed in @glyphs. Upon return, + * @num_glyphs should contain the number of generated glyphs. If the value + * @glyphs points at has changed after the call, the caller will free the + * allocated glyph array using cairo_glyph_free(). The caller will also free + * the original value of @glyphs, so this function shouldn't do so. + * + * If @clusters is not `NULL`, then @num_clusters and @cluster_flags + * should not be either, and @utf8 must be provided, and cluster + * mapping will be computed. The semantics of how + * cluster array allocation works is similar to the glyph array. That is, + * if @clusters initially points to a non-`NULL` value, that array may be used + * as a cluster buffer, and @num_clusters points to the number of cluster + * entries available there. If the provided cluster array is too short for + * the conversion (or for convenience), a new cluster array may be allocated + * using cairo_text_cluster_allocate() and placed in @clusters. In this case, + * the original value of @clusters will still be freed by the caller. Upon + * return, @num_clusters will contain the number of generated clusters. + * If the value @clusters points at has changed after the call, the caller + * will free the allocated cluster array using cairo_text_cluster_free(). + * + * See hb_cairo_font_face_set_scale_factor() for the details of + * the @scale_factor argument. + * + * The returned @glyphs vector actually has `@num_glyphs + 1` entries in + * it and the x,y values of the extra entry at the end add up the advance + * x,y of all the glyphs in the @buffer. + * + * Since: 7.0.0 + */ +void +hb_cairo_glyphs_from_buffer (hb_buffer_t *buffer, + hb_bool_t utf8_clusters, + double x_scale_factor, + double y_scale_factor, + double x, + double y, + const char *utf8, + int utf8_len, + cairo_glyph_t **glyphs, + unsigned int *num_glyphs, + cairo_text_cluster_t **clusters, + unsigned int *num_clusters, + cairo_text_cluster_flags_t *cluster_flags) +{ + if (utf8 && utf8_len < 0) + utf8_len = strlen (utf8); + + unsigned orig_num_glyphs = *num_glyphs; + *num_glyphs = hb_buffer_get_length (buffer); + hb_glyph_info_t *hb_glyph = hb_buffer_get_glyph_infos (buffer, nullptr); + hb_glyph_position_t *hb_position = hb_buffer_get_glyph_positions (buffer, nullptr); + if (orig_num_glyphs < *num_glyphs + 1) + *glyphs = cairo_glyph_allocate (*num_glyphs + 1); + + if (clusters && utf8) + { + unsigned orig_num_clusters = *num_clusters; + *num_clusters = *num_glyphs ? 1 : 0; + for (unsigned int i = 1; i < *num_glyphs; i++) + if (hb_glyph[i].cluster != hb_glyph[i-1].cluster) + (*num_clusters)++; + if (orig_num_clusters < *num_clusters) + *clusters = cairo_text_cluster_allocate (*num_clusters); + } + + double x_scale = x_scale_factor ? 1. / x_scale_factor : 0.; + double y_scale = y_scale_factor ? 1. / y_scale_factor : 0.; + hb_position_t hx = 0, hy = 0; + int i; + for (i = 0; i < (int) *num_glyphs; i++) + { + (*glyphs)[i].index = hb_glyph[i].codepoint; + (*glyphs)[i].x = x + (+hb_position->x_offset + hx) * x_scale; + (*glyphs)[i].y = y + (-hb_position->y_offset + hy) * y_scale; + hx += hb_position->x_advance; + hy += -hb_position->y_advance; + + hb_position++; + } + (*glyphs)[i].index = -1; + (*glyphs)[i].x = round (hx * x_scale); + (*glyphs)[i].y = round (hy * y_scale); + + if (clusters && *num_clusters && utf8) + { + hb_memset ((void *) *clusters, 0, *num_clusters * sizeof ((*clusters)[0])); + hb_bool_t backward = HB_DIRECTION_IS_BACKWARD (hb_buffer_get_direction (buffer)); + *cluster_flags = backward ? CAIRO_TEXT_CLUSTER_FLAG_BACKWARD : (cairo_text_cluster_flags_t) 0; + unsigned int cluster = 0; + const char *start = utf8, *end; + (*clusters)[cluster].num_glyphs++; + if (backward) + { + for (i = *num_glyphs - 2; i >= 0; i--) + { + if (hb_glyph[i].cluster != hb_glyph[i+1].cluster) + { + assert (hb_glyph[i].cluster > hb_glyph[i+1].cluster); + if (utf8_clusters) + end = start + hb_glyph[i].cluster - hb_glyph[i+1].cluster; + else + end = (const char *) hb_utf_offset_to_pointer ((const uint8_t *) start, + (signed) (hb_glyph[i].cluster - hb_glyph[i+1].cluster)); + (*clusters)[cluster].num_bytes = end - start; + start = end; + cluster++; + } + (*clusters)[cluster].num_glyphs++; + } + (*clusters)[cluster].num_bytes = utf8 + utf8_len - start; + } + else + { + for (i = 1; i < (int) *num_glyphs; i++) + { + if (hb_glyph[i].cluster != hb_glyph[i-1].cluster) + { + assert (hb_glyph[i].cluster > hb_glyph[i-1].cluster); + if (utf8_clusters) + end = start + hb_glyph[i].cluster - hb_glyph[i-1].cluster; + else + end = (const char *) hb_utf_offset_to_pointer ((const uint8_t *) start, + (signed) (hb_glyph[i].cluster - hb_glyph[i-1].cluster)); + (*clusters)[cluster].num_bytes = end - start; + start = end; + cluster++; + } + (*clusters)[cluster].num_glyphs++; + } + (*clusters)[cluster].num_bytes = utf8 + utf8_len - start; + } + } + else if (num_clusters) + *num_clusters = 0; +} + +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-cairo.h b/src/3rdparty/harfbuzz-ng/src/hb-cairo.h new file mode 100644 index 00000000000..21e284c8f93 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-cairo.h @@ -0,0 +1,99 @@ +/* + * Copyright © 2022 Red Hat, 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. + * + * Red Hat Author(s): Matthias Clasen + */ + +#ifndef HB_CAIRO_H +#define HB_CAIRO_H + +#include "hb.h" + +#include + +HB_BEGIN_DECLS + +HB_EXTERN cairo_font_face_t * +hb_cairo_font_face_create_for_font (hb_font_t *font); + +HB_EXTERN hb_font_t * +hb_cairo_font_face_get_font (cairo_font_face_t *font_face); + +HB_EXTERN cairo_font_face_t * +hb_cairo_font_face_create_for_face (hb_face_t *face); + +HB_EXTERN hb_face_t * +hb_cairo_font_face_get_face (cairo_font_face_t *font_face); + +/** + * hb_cairo_font_init_func_t: + * @font: The #hb_font_t being created + * @scaled_font: The respective #cairo_scaled_font_t + * @user_data: User data accompanying this method + * + * The type of a virtual method to be called when a cairo + * face created using hb_cairo_font_face_create_for_face() + * creates an #hb_font_t for a #cairo_scaled_font_t. + * + * Return value: the #hb_font_t value to use; in most cases same as @font + * + * Since: 7.0.0 + */ +typedef hb_font_t * (*hb_cairo_font_init_func_t) (hb_font_t *font, + cairo_scaled_font_t *scaled_font, + void *user_data); + +HB_EXTERN void +hb_cairo_font_face_set_font_init_func (cairo_font_face_t *font_face, + hb_cairo_font_init_func_t func, + void *user_data, + hb_destroy_func_t destroy); + +HB_EXTERN hb_font_t * +hb_cairo_scaled_font_get_font (cairo_scaled_font_t *scaled_font); + +HB_EXTERN void +hb_cairo_font_face_set_scale_factor (cairo_font_face_t *font_face, + unsigned int scale_factor); + +HB_EXTERN unsigned int +hb_cairo_font_face_get_scale_factor (cairo_font_face_t *font_face); + +HB_EXTERN void +hb_cairo_glyphs_from_buffer (hb_buffer_t *buffer, + hb_bool_t utf8_clusters, + double x_scale_factor, + double y_scale_factor, + double x, + double y, + const char *utf8, + int utf8_len, + cairo_glyph_t **glyphs, + unsigned int *num_glyphs, + cairo_text_cluster_t **clusters, + unsigned int *num_clusters, + cairo_text_cluster_flags_t *cluster_flags); + +HB_END_DECLS + +#endif /* HB_CAIRO_H */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-cff-interp-common.hh b/src/3rdparty/harfbuzz-ng/src/hb-cff-interp-common.hh new file mode 100644 index 00000000000..1d1f10f2bfa --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-cff-interp-common.hh @@ -0,0 +1,643 @@ +/* + * Copyright © 2018 Adobe 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. + * + * Adobe Author(s): Michiharu Ariza + */ +#ifndef HB_CFF_INTERP_COMMON_HH +#define HB_CFF_INTERP_COMMON_HH + +extern HB_INTERNAL const unsigned char *endchar_str; + +namespace CFF { + +using namespace OT; + +typedef unsigned int op_code_t; + + +/* === Dict operators === */ + +/* One byte operators (0-31) */ +#define OpCode_version 0 /* CFF Top */ +#define OpCode_Notice 1 /* CFF Top */ +#define OpCode_FullName 2 /* CFF Top */ +#define OpCode_FamilyName 3 /* CFF Top */ +#define OpCode_Weight 4 /* CFF Top */ +#define OpCode_FontBBox 5 /* CFF Top */ +#define OpCode_BlueValues 6 /* CFF Private, CFF2 Private */ +#define OpCode_OtherBlues 7 /* CFF Private, CFF2 Private */ +#define OpCode_FamilyBlues 8 /* CFF Private, CFF2 Private */ +#define OpCode_FamilyOtherBlues 9 /* CFF Private, CFF2 Private */ +#define OpCode_StdHW 10 /* CFF Private, CFF2 Private */ +#define OpCode_StdVW 11 /* CFF Private, CFF2 Private */ +#define OpCode_escape 12 /* All. Shared with CS */ +#define OpCode_UniqueID 13 /* CFF Top */ +#define OpCode_XUID 14 /* CFF Top */ +#define OpCode_charset 15 /* CFF Top (0) */ +#define OpCode_Encoding 16 /* CFF Top (0) */ +#define OpCode_CharStrings 17 /* CFF Top, CFF2 Top */ +#define OpCode_Private 18 /* CFF Top, CFF2 FD */ +#define OpCode_Subrs 19 /* CFF Private, CFF2 Private */ +#define OpCode_defaultWidthX 20 /* CFF Private (0) */ +#define OpCode_nominalWidthX 21 /* CFF Private (0) */ +#define OpCode_vsindexdict 22 /* CFF2 Private/CS */ +#define OpCode_blenddict 23 /* CFF2 Private/CS */ +#define OpCode_vstore 24 /* CFF2 Top */ +#define OpCode_reserved25 25 +#define OpCode_reserved26 26 +#define OpCode_reserved27 27 + +/* Numbers */ +#define OpCode_shortint 28 /* 16-bit integer, All */ +#define OpCode_longintdict 29 /* 32-bit integer, All */ +#define OpCode_BCD 30 /* Real number, CFF2 Top/FD */ +#define OpCode_reserved31 31 + +/* 1-byte integers */ +#define OpCode_OneByteIntFirst 32 /* All. beginning of the range of first byte ints */ +#define OpCode_OneByteIntLast 246 /* All. ending of the range of first byte int */ + +/* 2-byte integers */ +#define OpCode_TwoBytePosInt0 247 /* All. first byte of two byte positive int (+108 to +1131) */ +#define OpCode_TwoBytePosInt1 248 +#define OpCode_TwoBytePosInt2 249 +#define OpCode_TwoBytePosInt3 250 + +#define OpCode_TwoByteNegInt0 251 /* All. first byte of two byte negative int (-1131 to -108) */ +#define OpCode_TwoByteNegInt1 252 +#define OpCode_TwoByteNegInt2 253 +#define OpCode_TwoByteNegInt3 254 + +/* Two byte escape operators 12, (0-41) */ +#define OpCode_ESC_Base 256 +#define Make_OpCode_ESC(byte2) ((op_code_t)(OpCode_ESC_Base + (byte2))) + +inline op_code_t Unmake_OpCode_ESC (op_code_t op) { return (op_code_t)(op - OpCode_ESC_Base); } +inline bool Is_OpCode_ESC (op_code_t op) { return op >= OpCode_ESC_Base; } +inline unsigned int OpCode_Size (op_code_t op) { return Is_OpCode_ESC (op) ? 2: 1; } + +#define OpCode_Copyright Make_OpCode_ESC(0) /* CFF Top */ +#define OpCode_isFixedPitch Make_OpCode_ESC(1) /* CFF Top (false) */ +#define OpCode_ItalicAngle Make_OpCode_ESC(2) /* CFF Top (0) */ +#define OpCode_UnderlinePosition Make_OpCode_ESC(3) /* CFF Top (-100) */ +#define OpCode_UnderlineThickness Make_OpCode_ESC(4) /* CFF Top (50) */ +#define OpCode_PaintType Make_OpCode_ESC(5) /* CFF Top (0) */ +#define OpCode_CharstringType Make_OpCode_ESC(6) /* CFF Top (2) */ +#define OpCode_FontMatrix Make_OpCode_ESC(7) /* CFF Top, CFF2 Top (.001 0 0 .001 0 0)*/ +#define OpCode_StrokeWidth Make_OpCode_ESC(8) /* CFF Top (0) */ +#define OpCode_BlueScale Make_OpCode_ESC(9) /* CFF Private, CFF2 Private (0.039625) */ +#define OpCode_BlueShift Make_OpCode_ESC(10) /* CFF Private, CFF2 Private (7) */ +#define OpCode_BlueFuzz Make_OpCode_ESC(11) /* CFF Private, CFF2 Private (1) */ +#define OpCode_StemSnapH Make_OpCode_ESC(12) /* CFF Private, CFF2 Private */ +#define OpCode_StemSnapV Make_OpCode_ESC(13) /* CFF Private, CFF2 Private */ +#define OpCode_ForceBold Make_OpCode_ESC(14) /* CFF Private (false) */ +#define OpCode_reservedESC15 Make_OpCode_ESC(15) +#define OpCode_reservedESC16 Make_OpCode_ESC(16) +#define OpCode_LanguageGroup Make_OpCode_ESC(17) /* CFF Private, CFF2 Private (0) */ +#define OpCode_ExpansionFactor Make_OpCode_ESC(18) /* CFF Private, CFF2 Private (0.06) */ +#define OpCode_initialRandomSeed Make_OpCode_ESC(19) /* CFF Private (0) */ +#define OpCode_SyntheticBase Make_OpCode_ESC(20) /* CFF Top */ +#define OpCode_PostScript Make_OpCode_ESC(21) /* CFF Top */ +#define OpCode_BaseFontName Make_OpCode_ESC(22) /* CFF Top */ +#define OpCode_BaseFontBlend Make_OpCode_ESC(23) /* CFF Top */ +#define OpCode_reservedESC24 Make_OpCode_ESC(24) +#define OpCode_reservedESC25 Make_OpCode_ESC(25) +#define OpCode_reservedESC26 Make_OpCode_ESC(26) +#define OpCode_reservedESC27 Make_OpCode_ESC(27) +#define OpCode_reservedESC28 Make_OpCode_ESC(28) +#define OpCode_reservedESC29 Make_OpCode_ESC(29) +#define OpCode_ROS Make_OpCode_ESC(30) /* CFF Top_CID */ +#define OpCode_CIDFontVersion Make_OpCode_ESC(31) /* CFF Top_CID (0) */ +#define OpCode_CIDFontRevision Make_OpCode_ESC(32) /* CFF Top_CID (0) */ +#define OpCode_CIDFontType Make_OpCode_ESC(33) /* CFF Top_CID (0) */ +#define OpCode_CIDCount Make_OpCode_ESC(34) /* CFF Top_CID (8720) */ +#define OpCode_UIDBase Make_OpCode_ESC(35) /* CFF Top_CID */ +#define OpCode_FDArray Make_OpCode_ESC(36) /* CFF Top_CID, CFF2 Top */ +#define OpCode_FDSelect Make_OpCode_ESC(37) /* CFF Top_CID, CFF2 Top */ +#define OpCode_FontName Make_OpCode_ESC(38) /* CFF Top_CID */ + + +/* === CharString operators === */ + +#define OpCode_hstem 1 /* CFF, CFF2 */ +#define OpCode_Reserved2 2 +#define OpCode_vstem 3 /* CFF, CFF2 */ +#define OpCode_vmoveto 4 /* CFF, CFF2 */ +#define OpCode_rlineto 5 /* CFF, CFF2 */ +#define OpCode_hlineto 6 /* CFF, CFF2 */ +#define OpCode_vlineto 7 /* CFF, CFF2 */ +#define OpCode_rrcurveto 8 /* CFF, CFF2 */ +#define OpCode_Reserved9 9 +#define OpCode_callsubr 10 /* CFF, CFF2 */ +#define OpCode_return 11 /* CFF */ +//#define OpCode_escape 12 /* CFF, CFF2 */ +#define OpCode_Reserved13 13 +#define OpCode_endchar 14 /* CFF */ +#define OpCode_vsindexcs 15 /* CFF2 */ +#define OpCode_blendcs 16 /* CFF2 */ +#define OpCode_Reserved17 17 +#define OpCode_hstemhm 18 /* CFF, CFF2 */ +#define OpCode_hintmask 19 /* CFF, CFF2 */ +#define OpCode_cntrmask 20 /* CFF, CFF2 */ +#define OpCode_rmoveto 21 /* CFF, CFF2 */ +#define OpCode_hmoveto 22 /* CFF, CFF2 */ +#define OpCode_vstemhm 23 /* CFF, CFF2 */ +#define OpCode_rcurveline 24 /* CFF, CFF2 */ +#define OpCode_rlinecurve 25 /* CFF, CFF2 */ +#define OpCode_vvcurveto 26 /* CFF, CFF2 */ +#define OpCode_hhcurveto 27 /* CFF, CFF2 */ +//#define OpCode_shortint 28 /* CFF, CFF2 */ +#define OpCode_callgsubr 29 /* CFF, CFF2 */ +#define OpCode_vhcurveto 30 /* CFF, CFF2 */ +#define OpCode_hvcurveto 31 /* CFF, CFF2 */ + +#define OpCode_fixedcs 255 /* 32-bit fixed */ + +/* Two byte escape operators 12, (0-41) */ +#define OpCode_dotsection Make_OpCode_ESC(0) /* CFF (obsoleted) */ +#define OpCode_ReservedESC1 Make_OpCode_ESC(1) +#define OpCode_ReservedESC2 Make_OpCode_ESC(2) +#define OpCode_and Make_OpCode_ESC(3) /* CFF */ +#define OpCode_or Make_OpCode_ESC(4) /* CFF */ +#define OpCode_not Make_OpCode_ESC(5) /* CFF */ +#define OpCode_ReservedESC6 Make_OpCode_ESC(6) +#define OpCode_ReservedESC7 Make_OpCode_ESC(7) +#define OpCode_ReservedESC8 Make_OpCode_ESC(8) +#define OpCode_abs Make_OpCode_ESC(9) /* CFF */ +#define OpCode_add Make_OpCode_ESC(10) /* CFF */ +#define OpCode_sub Make_OpCode_ESC(11) /* CFF */ +#define OpCode_div Make_OpCode_ESC(12) /* CFF */ +#define OpCode_ReservedESC13 Make_OpCode_ESC(13) +#define OpCode_neg Make_OpCode_ESC(14) /* CFF */ +#define OpCode_eq Make_OpCode_ESC(15) /* CFF */ +#define OpCode_ReservedESC16 Make_OpCode_ESC(16) +#define OpCode_ReservedESC17 Make_OpCode_ESC(17) +#define OpCode_drop Make_OpCode_ESC(18) /* CFF */ +#define OpCode_ReservedESC19 Make_OpCode_ESC(19) +#define OpCode_put Make_OpCode_ESC(20) /* CFF */ +#define OpCode_get Make_OpCode_ESC(21) /* CFF */ +#define OpCode_ifelse Make_OpCode_ESC(22) /* CFF */ +#define OpCode_random Make_OpCode_ESC(23) /* CFF */ +#define OpCode_mul Make_OpCode_ESC(24) /* CFF */ +//#define OpCode_reservedESC25 Make_OpCode_ESC(25) +#define OpCode_sqrt Make_OpCode_ESC(26) /* CFF */ +#define OpCode_dup Make_OpCode_ESC(27) /* CFF */ +#define OpCode_exch Make_OpCode_ESC(28) /* CFF */ +#define OpCode_index Make_OpCode_ESC(29) /* CFF */ +#define OpCode_roll Make_OpCode_ESC(30) /* CFF */ +#define OpCode_reservedESC31 Make_OpCode_ESC(31) +#define OpCode_reservedESC32 Make_OpCode_ESC(32) +#define OpCode_reservedESC33 Make_OpCode_ESC(33) +#define OpCode_hflex Make_OpCode_ESC(34) /* CFF, CFF2 */ +#define OpCode_flex Make_OpCode_ESC(35) /* CFF, CFF2 */ +#define OpCode_hflex1 Make_OpCode_ESC(36) /* CFF, CFF2 */ +#define OpCode_flex1 Make_OpCode_ESC(37) /* CFF, CFF2 */ + + +#define OpCode_Invalid 0xFFFFu + + +struct number_t +{ + void set_int (int v) { value = v; } + int to_int () const { return value; } + + void set_fixed (int32_t v) { value = v / 65536.0; } + int32_t to_fixed () const { return value * 65536.0; } + + void set_real (double v) { value = v; } + double to_real () const { return value; } + + bool in_int_range () const + { return ((double) (int16_t) to_int () == value); } + + bool operator > (const number_t &n) const { return value > n.to_real (); } + bool operator < (const number_t &n) const { return n > *this; } + bool operator >= (const number_t &n) const { return !(*this < n); } + bool operator <= (const number_t &n) const { return !(*this > n); } + + const number_t &operator += (const number_t &n) + { + set_real (to_real () + n.to_real ()); + + return *this; + } + + protected: + double value = 0.; +}; + +/* byte string */ +struct UnsizedByteStr : UnsizedArrayOf +{ + hb_ubytes_t as_ubytes (unsigned l) const + { return hb_ubytes_t ((const unsigned char *) this, l); } + + // encode 2-byte int (Dict/CharString) or 4-byte int (Dict) + template + static bool serialize_int (hb_serialize_context_t *c, op_code_t intOp, V value) + { + TRACE_SERIALIZE (this); + + HBUINT8 *p = c->allocate_size (1); + if (unlikely (!p)) return_trace (false); + *p = intOp; + + T *ip = c->allocate_size (T::static_size); + if (unlikely (!ip)) return_trace (false); + return_trace (c->check_assign (*ip, value, HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + + template + static bool serialize_int4 (hb_serialize_context_t *c, V value) + { return serialize_int (c, OpCode_longintdict, value); } + + template + static bool serialize_int2 (hb_serialize_context_t *c, V value) + { return serialize_int (c, OpCode_shortint, value); } + + /* Defining null_size allows a Null object may be created. Should be safe because: + * A descendent struct Dict uses a Null pointer to indicate a missing table, + * checked before access. + */ + DEFINE_SIZE_MIN(0); +}; + +/* A byte string associated with the current offset and an error condition */ +struct byte_str_ref_t +{ + byte_str_ref_t () + : str () {} + + byte_str_ref_t (const hb_ubytes_t &str_, unsigned int offset_ = 0) + : str (str_) { set_offset (offset_); } + + void reset (const hb_ubytes_t &str_, unsigned int offset_ = 0) + { + str = str_; + set_offset (offset_); + } + + const unsigned char& operator [] (int i) { + if (unlikely ((unsigned int) (get_offset () + i) >= str.length)) + { + set_error (); + return Null (unsigned char); + } + return str.arrayZ[get_offset () + i]; + } + + unsigned char head_unchecked () const { return str.arrayZ[get_offset ()]; } + + /* Conversion to hb_ubytes_t */ + operator hb_ubytes_t () const { return str.sub_array (get_offset ()); } + + hb_ubytes_t sub_array (unsigned int offset_, unsigned int len_) const + { return str.sub_array (offset_, len_); } + + bool avail (unsigned int count=1) const + { return get_offset () + count <= str.length; } + void inc (unsigned int count=1) + { + /* Automatically puts us in error if count is out-of-range. */ + set_offset (get_offset () + count); + } + + /* We (ab)use ubytes backwards_length as a cursor (called offset), + * as well as to store error condition. */ + + unsigned get_offset () const { return str.backwards_length; } + void set_offset (unsigned offset) { str.backwards_length = offset; } + + void set_error () { str.backwards_length = str.length + 1; } + bool in_error () const { return str.backwards_length > str.length; } + + unsigned total_size () const { return str.length; } + + protected: + hb_ubytes_t str; +}; + +/* stack */ +template +struct cff_stack_t +{ + ELEM& operator [] (unsigned int i) + { + if (unlikely (i >= count)) + { + set_error (); + return Crap (ELEM); + } + return elements[i]; + } + + void push (const ELEM &v) + { + if (likely (count < LIMIT)) + elements[count++] = v; + else + set_error (); + } + ELEM &push () + { + if (likely (count < LIMIT)) + return elements[count++]; + else + { + set_error (); + return Crap (ELEM); + } + } + + ELEM& pop () + { + if (likely (count > 0)) + return elements[--count]; + else + { + set_error (); + return Crap (ELEM); + } + } + void pop (unsigned int n) + { + if (likely (count >= n)) + count -= n; + else + set_error (); + } + + const ELEM& peek () + { + if (unlikely (count == 0)) + { + set_error (); + return Null (ELEM); + } + return elements[count - 1]; + } + + void unpop () + { + if (likely (count < LIMIT)) + count++; + else + set_error (); + } + + void clear () { count = 0; } + + bool in_error () const { return (error); } + void set_error () { error = true; } + + unsigned int get_count () const { return count; } + bool is_empty () const { return !count; } + + hb_array_t sub_array (unsigned start, unsigned length) const + { return hb_array_t (elements).sub_array (start, length); } + + private: + bool error = false; + unsigned int count = 0; + ELEM elements[LIMIT]; +}; + +/* argument stack */ +template +struct arg_stack_t : cff_stack_t +{ + void push_int (int v) + { + ARG &n = S::push (); + n.set_int (v); + } + + void push_fixed (int32_t v) + { + ARG &n = S::push (); + n.set_fixed (v); + } + + void push_real (double v) + { + ARG &n = S::push (); + n.set_real (v); + } + + ARG& pop_num () { return this->pop (); } + + int pop_int () { return this->pop ().to_int (); } + + unsigned int pop_uint () + { + int i = pop_int (); + if (unlikely (i < 0)) + { + i = 0; + S::set_error (); + } + return (unsigned) i; + } + + void push_longint_from_substr (byte_str_ref_t& str_ref) + { + push_int ((str_ref[0] << 24) | (str_ref[1] << 16) | (str_ref[2] << 8) | (str_ref[3])); + str_ref.inc (4); + } + + bool push_fixed_from_substr (byte_str_ref_t& str_ref) + { + if (unlikely (!str_ref.avail (4))) + return false; + push_fixed ((int32_t)*(const HBUINT32*)&str_ref[0]); + str_ref.inc (4); + return true; + } + + private: + typedef cff_stack_t S; +}; + +/* an operator prefixed by its operands in a byte string */ +struct op_str_t +{ + /* This used to have a hb_ubytes_t. Using a pointer and length + * in a particular order, saves 8 bytes in this struct and more + * in our parsed_cs_op_t subclass. */ + + const unsigned char *ptr = nullptr; + + op_code_t op = OpCode_Invalid; + + uint8_t length = 0; +}; + +/* base of OP_SERIALIZER */ +struct op_serializer_t +{ + protected: + bool copy_opstr (hb_serialize_context_t *c, const op_str_t& opstr) const + { + TRACE_SERIALIZE (this); + + unsigned char *d = c->allocate_size (opstr.length); + if (unlikely (!d)) return_trace (false); + /* Faster than hb_memcpy for small strings. */ + for (unsigned i = 0; i < opstr.length; i++) + d[i] = opstr.ptr[i]; + return_trace (true); + } +}; + +template +struct parsed_values_t +{ + void init () + { + opStart = 0; + values.init (); + } + void fini () { values.fini (); } + + void alloc (unsigned n) + { + values.alloc (n, true); + } + + void add_op (op_code_t op, const byte_str_ref_t& str_ref = byte_str_ref_t (), const VAL &v = VAL ()) + { + VAL *val = values.push (v); + val->op = op; + auto arr = str_ref.sub_array (opStart, str_ref.get_offset () - opStart); + val->ptr = arr.arrayZ; + val->length = arr.length; + opStart = str_ref.get_offset (); + } + + bool has_op (op_code_t op) const + { + for (const auto& v : values) + if (v.op == op) return true; + return false; + } + + unsigned get_count () const { return values.length; } + const VAL &operator [] (unsigned int i) const { return values[i]; } + + unsigned int opStart; + hb_vector_t values; +}; + +template +struct interp_env_t +{ + interp_env_t () {} + interp_env_t (const hb_ubytes_t &str_) + { + str_ref.reset (str_); + } + bool in_error () const + { return str_ref.in_error () || argStack.in_error (); } + + void set_error () { str_ref.set_error (); } + + op_code_t fetch_op () + { + op_code_t op = OpCode_Invalid; + if (unlikely (!str_ref.avail ())) + return OpCode_Invalid; + op = (op_code_t) str_ref.head_unchecked (); + str_ref.inc (); + if (op == OpCode_escape) { + if (unlikely (!str_ref.avail ())) + return OpCode_Invalid; + op = Make_OpCode_ESC (str_ref.head_unchecked ()); + str_ref.inc (); + } + return op; + } + + const ARG& eval_arg (unsigned int i) { return argStack[i]; } + + ARG& pop_arg () { return argStack.pop (); } + void pop_n_args (unsigned int n) { argStack.pop (n); } + + void clear_args () { pop_n_args (argStack.get_count ()); } + + byte_str_ref_t + str_ref; + arg_stack_t + argStack; +}; + +using num_interp_env_t = interp_env_t<>; + +template +struct opset_t +{ + static void process_op (op_code_t op, interp_env_t& env) + { + switch (op) { + case OpCode_shortint: + env.argStack.push_int ((int16_t)((env.str_ref[0] << 8) | env.str_ref[1])); + env.str_ref.inc (2); + break; + + case OpCode_TwoBytePosInt0: case OpCode_TwoBytePosInt1: + case OpCode_TwoBytePosInt2: case OpCode_TwoBytePosInt3: + env.argStack.push_int ((int16_t)((op - OpCode_TwoBytePosInt0) * 256 + env.str_ref[0] + 108)); + env.str_ref.inc (); + break; + + case OpCode_TwoByteNegInt0: case OpCode_TwoByteNegInt1: + case OpCode_TwoByteNegInt2: case OpCode_TwoByteNegInt3: + env.argStack.push_int ((-(int16_t)(op - OpCode_TwoByteNegInt0) * 256 - env.str_ref[0] - 108)); + env.str_ref.inc (); + break; + + default: + /* 1-byte integer */ + if (likely ((OpCode_OneByteIntFirst <= op) && (op <= OpCode_OneByteIntLast))) + { + env.argStack.push_int ((int)op - 139); + } else { + /* invalid unknown operator */ + env.clear_args (); + env.set_error (); + } + break; + } + } +}; + +template +struct interpreter_t +{ + interpreter_t (ENV& env_) : env (env_) {} + ENV& env; +}; + +} /* namespace CFF */ + +#endif /* HB_CFF_INTERP_COMMON_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-cff-interp-cs-common.hh b/src/3rdparty/harfbuzz-ng/src/hb-cff-interp-cs-common.hh new file mode 100644 index 00000000000..28a777eb0d4 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-cff-interp-cs-common.hh @@ -0,0 +1,905 @@ +/* + * Copyright © 2018 Adobe 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. + * + * Adobe Author(s): Michiharu Ariza + */ +#ifndef HB_CFF_INTERP_CS_COMMON_HH +#define HB_CFF_INTERP_CS_COMMON_HH + +#include "hb.hh" +#include "hb-cff-interp-common.hh" + +namespace CFF { + +using namespace OT; + +enum cs_type_t { + CSType_CharString, + CSType_GlobalSubr, + CSType_LocalSubr +}; + +struct call_context_t +{ + void init (const byte_str_ref_t substr_=byte_str_ref_t (), cs_type_t type_=CSType_CharString, unsigned int subr_num_=0) + { + str_ref = substr_; + type = type_; + subr_num = subr_num_; + } + + void fini () {} + + byte_str_ref_t str_ref; + cs_type_t type; + unsigned int subr_num; +}; + +/* call stack */ +const unsigned int kMaxCallLimit = 10; +struct call_stack_t : cff_stack_t {}; + +template +struct biased_subrs_t +{ + void init (const SUBRS *subrs_) + { + subrs = subrs_; + unsigned int nSubrs = get_count (); + if (nSubrs < 1240) + bias = 107; + else if (nSubrs < 33900) + bias = 1131; + else + bias = 32768; + } + + void fini () {} + + unsigned int get_count () const { return subrs ? subrs->count : 0; } + unsigned int get_bias () const { return bias; } + + hb_ubytes_t operator [] (unsigned int index) const + { + if (unlikely (!subrs || index >= subrs->count)) + return hb_ubytes_t (); + else + return (*subrs)[index]; + } + + protected: + unsigned int bias; + const SUBRS *subrs; +}; + +struct point_t +{ + void set_int (int _x, int _y) + { + x.set_int (_x); + y.set_int (_y); + } + + void move_x (const number_t &dx) { x += dx; } + void move_y (const number_t &dy) { y += dy; } + void move (const number_t &dx, const number_t &dy) { move_x (dx); move_y (dy); } + void move (const point_t &d) { move_x (d.x); move_y (d.y); } + + number_t x; + number_t y; +}; + +template +struct cs_interp_env_t : interp_env_t +{ + cs_interp_env_t (const hb_ubytes_t &str, const SUBRS *globalSubrs_, const SUBRS *localSubrs_) : + interp_env_t (str) + { + context.init (str, CSType_CharString); + seen_moveto = true; + seen_hintmask = false; + hstem_count = 0; + vstem_count = 0; + hintmask_size = 0; + pt.set_int (0, 0); + globalSubrs.init (globalSubrs_); + localSubrs.init (localSubrs_); + } + ~cs_interp_env_t () + { + globalSubrs.fini (); + localSubrs.fini (); + } + + bool in_error () const + { + return callStack.in_error () || SUPER::in_error (); + } + + bool pop_subr_num (const biased_subrs_t& biasedSubrs, unsigned int &subr_num) + { + subr_num = 0; + int n = SUPER::argStack.pop_int (); + n += biasedSubrs.get_bias (); + if (unlikely ((n < 0) || ((unsigned int)n >= biasedSubrs.get_count ()))) + return false; + + subr_num = (unsigned int)n; + return true; + } + + void call_subr (const biased_subrs_t& biasedSubrs, cs_type_t type) + { + unsigned int subr_num = 0; + + if (unlikely (!pop_subr_num (biasedSubrs, subr_num) + || callStack.get_count () >= kMaxCallLimit)) + { + SUPER::set_error (); + return; + } + context.str_ref = SUPER::str_ref; + callStack.push (context); + + context.init ( biasedSubrs[subr_num], type, subr_num); + SUPER::str_ref = context.str_ref; + } + + void return_from_subr () + { + if (unlikely (SUPER::str_ref.in_error ())) + SUPER::set_error (); + context = callStack.pop (); + SUPER::str_ref = context.str_ref; + } + + void determine_hintmask_size () + { + if (!seen_hintmask) + { + vstem_count += SUPER::argStack.get_count() / 2; + hintmask_size = (hstem_count + vstem_count + 7) >> 3; + seen_hintmask = true; + } + } + + void set_endchar (bool endchar_flag_) { endchar_flag = endchar_flag_; } + bool is_endchar () const { return endchar_flag; } + + const number_t &get_x () const { return pt.x; } + const number_t &get_y () const { return pt.y; } + const point_t &get_pt () const { return pt; } + + void moveto (const point_t &pt_ ) { pt = pt_; } + + public: + call_context_t context; + bool endchar_flag; + bool seen_moveto; + bool seen_hintmask; + + unsigned int hstem_count; + unsigned int vstem_count; + unsigned int hintmask_size; + call_stack_t callStack; + biased_subrs_t globalSubrs; + biased_subrs_t localSubrs; + + private: + point_t pt; + + typedef interp_env_t SUPER; +}; + +template +struct path_procs_null_t +{ + static void rmoveto (ENV &env, PARAM& param) {} + static void hmoveto (ENV &env, PARAM& param) {} + static void vmoveto (ENV &env, PARAM& param) {} + static void rlineto (ENV &env, PARAM& param) {} + static void hlineto (ENV &env, PARAM& param) {} + static void vlineto (ENV &env, PARAM& param) {} + static void rrcurveto (ENV &env, PARAM& param) {} + static void rcurveline (ENV &env, PARAM& param) {} + static void rlinecurve (ENV &env, PARAM& param) {} + static void vvcurveto (ENV &env, PARAM& param) {} + static void hhcurveto (ENV &env, PARAM& param) {} + static void vhcurveto (ENV &env, PARAM& param) {} + static void hvcurveto (ENV &env, PARAM& param) {} + static void moveto (ENV &env, PARAM& param, const point_t &pt) {} + static void line (ENV &env, PARAM& param, const point_t &pt1) {} + static void curve (ENV &env, PARAM& param, const point_t &pt1, const point_t &pt2, const point_t &pt3) {} + static void hflex (ENV &env, PARAM& param) {} + static void flex (ENV &env, PARAM& param) {} + static void hflex1 (ENV &env, PARAM& param) {} + static void flex1 (ENV &env, PARAM& param) {} +}; + +template > +struct cs_opset_t : opset_t +{ + static void process_op (op_code_t op, ENV &env, PARAM& param) + { + switch (op) { + + case OpCode_return: + env.return_from_subr (); + break; + case OpCode_endchar: + OPSET::check_width (op, env, param); + env.set_endchar (true); + OPSET::flush_args_and_op (op, env, param); + break; + + case OpCode_fixedcs: + env.argStack.push_fixed_from_substr (env.str_ref); + break; + + case OpCode_callsubr: + env.call_subr (env.localSubrs, CSType_LocalSubr); + break; + + case OpCode_callgsubr: + env.call_subr (env.globalSubrs, CSType_GlobalSubr); + break; + + case OpCode_hstem: + case OpCode_hstemhm: + OPSET::check_width (op, env, param); + OPSET::process_hstem (op, env, param); + break; + case OpCode_vstem: + case OpCode_vstemhm: + OPSET::check_width (op, env, param); + OPSET::process_vstem (op, env, param); + break; + case OpCode_hintmask: + case OpCode_cntrmask: + OPSET::check_width (op, env, param); + OPSET::process_hintmask (op, env, param); + break; + case OpCode_rmoveto: + OPSET::check_width (op, env, param); + PATH::rmoveto (env, param); + OPSET::process_post_move (op, env, param); + break; + case OpCode_hmoveto: + OPSET::check_width (op, env, param); + PATH::hmoveto (env, param); + OPSET::process_post_move (op, env, param); + break; + case OpCode_vmoveto: + OPSET::check_width (op, env, param); + PATH::vmoveto (env, param); + OPSET::process_post_move (op, env, param); + break; + case OpCode_rlineto: + PATH::rlineto (env, param); + process_post_path (op, env, param); + break; + case OpCode_hlineto: + PATH::hlineto (env, param); + process_post_path (op, env, param); + break; + case OpCode_vlineto: + PATH::vlineto (env, param); + process_post_path (op, env, param); + break; + case OpCode_rrcurveto: + PATH::rrcurveto (env, param); + process_post_path (op, env, param); + break; + case OpCode_rcurveline: + PATH::rcurveline (env, param); + process_post_path (op, env, param); + break; + case OpCode_rlinecurve: + PATH::rlinecurve (env, param); + process_post_path (op, env, param); + break; + case OpCode_vvcurveto: + PATH::vvcurveto (env, param); + process_post_path (op, env, param); + break; + case OpCode_hhcurveto: + PATH::hhcurveto (env, param); + process_post_path (op, env, param); + break; + case OpCode_vhcurveto: + PATH::vhcurveto (env, param); + process_post_path (op, env, param); + break; + case OpCode_hvcurveto: + PATH::hvcurveto (env, param); + process_post_path (op, env, param); + break; + + case OpCode_hflex: + PATH::hflex (env, param); + OPSET::process_post_flex (op, env, param); + break; + + case OpCode_flex: + PATH::flex (env, param); + OPSET::process_post_flex (op, env, param); + break; + + case OpCode_hflex1: + PATH::hflex1 (env, param); + OPSET::process_post_flex (op, env, param); + break; + + case OpCode_flex1: + PATH::flex1 (env, param); + OPSET::process_post_flex (op, env, param); + break; + + default: + SUPER::process_op (op, env); + break; + } + } + + static void process_hstem (op_code_t op, ENV &env, PARAM& param) + { + env.hstem_count += env.argStack.get_count () / 2; + OPSET::flush_args_and_op (op, env, param); + } + + static void process_vstem (op_code_t op, ENV &env, PARAM& param) + { + env.vstem_count += env.argStack.get_count () / 2; + OPSET::flush_args_and_op (op, env, param); + } + + static void process_hintmask (op_code_t op, ENV &env, PARAM& param) + { + env.determine_hintmask_size (); + if (likely (env.str_ref.avail (env.hintmask_size))) + { + OPSET::flush_hintmask (op, env, param); + env.str_ref.inc (env.hintmask_size); + } + } + + static void process_post_flex (op_code_t op, ENV &env, PARAM& param) + { + OPSET::flush_args_and_op (op, env, param); + } + + static void check_width (op_code_t op, ENV &env, PARAM& param) + {} + + static void process_post_move (op_code_t op, ENV &env, PARAM& param) + { + if (!env.seen_moveto) + { + env.determine_hintmask_size (); + env.seen_moveto = true; + } + OPSET::flush_args_and_op (op, env, param); + } + + static void process_post_path (op_code_t op, ENV &env, PARAM& param) + { + OPSET::flush_args_and_op (op, env, param); + } + + static void flush_args_and_op (op_code_t op, ENV &env, PARAM& param) + { + OPSET::flush_args (env, param); + OPSET::flush_op (op, env, param); + } + + static void flush_args (ENV &env, PARAM& param) + { + env.pop_n_args (env.argStack.get_count ()); + } + + static void flush_op (op_code_t op, ENV &env, PARAM& param) + { + } + + static void flush_hintmask (op_code_t op, ENV &env, PARAM& param) + { + OPSET::flush_args_and_op (op, env, param); + } + + static bool is_number_op (op_code_t op) + { + switch (op) + { + case OpCode_shortint: + case OpCode_fixedcs: + case OpCode_TwoBytePosInt0: case OpCode_TwoBytePosInt1: + case OpCode_TwoBytePosInt2: case OpCode_TwoBytePosInt3: + case OpCode_TwoByteNegInt0: case OpCode_TwoByteNegInt1: + case OpCode_TwoByteNegInt2: case OpCode_TwoByteNegInt3: + return true; + + default: + /* 1-byte integer */ + return (OpCode_OneByteIntFirst <= op) && (op <= OpCode_OneByteIntLast); + } + } + + protected: + typedef opset_t SUPER; +}; + +template +struct path_procs_t +{ + static void rmoveto (ENV &env, PARAM& param) + { + point_t pt1 = env.get_pt (); + const number_t &dy = env.pop_arg (); + const number_t &dx = env.pop_arg (); + pt1.move (dx, dy); + PATH::moveto (env, param, pt1); + } + + static void hmoveto (ENV &env, PARAM& param) + { + point_t pt1 = env.get_pt (); + pt1.move_x (env.pop_arg ()); + PATH::moveto (env, param, pt1); + } + + static void vmoveto (ENV &env, PARAM& param) + { + point_t pt1 = env.get_pt (); + pt1.move_y (env.pop_arg ()); + PATH::moveto (env, param, pt1); + } + + static void rlineto (ENV &env, PARAM& param) + { + for (unsigned int i = 0; i + 2 <= env.argStack.get_count (); i += 2) + { + point_t pt1 = env.get_pt (); + pt1.move (env.eval_arg (i), env.eval_arg (i+1)); + PATH::line (env, param, pt1); + } + } + + static void hlineto (ENV &env, PARAM& param) + { + point_t pt1; + unsigned int i = 0; + for (; i + 2 <= env.argStack.get_count (); i += 2) + { + pt1 = env.get_pt (); + pt1.move_x (env.eval_arg (i)); + PATH::line (env, param, pt1); + pt1.move_y (env.eval_arg (i+1)); + PATH::line (env, param, pt1); + } + if (i < env.argStack.get_count ()) + { + pt1 = env.get_pt (); + pt1.move_x (env.eval_arg (i)); + PATH::line (env, param, pt1); + } + } + + static void vlineto (ENV &env, PARAM& param) + { + point_t pt1; + unsigned int i = 0; + for (; i + 2 <= env.argStack.get_count (); i += 2) + { + pt1 = env.get_pt (); + pt1.move_y (env.eval_arg (i)); + PATH::line (env, param, pt1); + pt1.move_x (env.eval_arg (i+1)); + PATH::line (env, param, pt1); + } + if (i < env.argStack.get_count ()) + { + pt1 = env.get_pt (); + pt1.move_y (env.eval_arg (i)); + PATH::line (env, param, pt1); + } + } + + static void rrcurveto (ENV &env, PARAM& param) + { + for (unsigned int i = 0; i + 6 <= env.argStack.get_count (); i += 6) + { + point_t pt1 = env.get_pt (); + pt1.move (env.eval_arg (i), env.eval_arg (i+1)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (i+2), env.eval_arg (i+3)); + point_t pt3 = pt2; + pt3.move (env.eval_arg (i+4), env.eval_arg (i+5)); + PATH::curve (env, param, pt1, pt2, pt3); + } + } + + static void rcurveline (ENV &env, PARAM& param) + { + unsigned int arg_count = env.argStack.get_count (); + if (unlikely (arg_count < 8)) + return; + + unsigned int i = 0; + unsigned int curve_limit = arg_count - 2; + for (; i + 6 <= curve_limit; i += 6) + { + point_t pt1 = env.get_pt (); + pt1.move (env.eval_arg (i), env.eval_arg (i+1)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (i+2), env.eval_arg (i+3)); + point_t pt3 = pt2; + pt3.move (env.eval_arg (i+4), env.eval_arg (i+5)); + PATH::curve (env, param, pt1, pt2, pt3); + } + + point_t pt1 = env.get_pt (); + pt1.move (env.eval_arg (i), env.eval_arg (i+1)); + PATH::line (env, param, pt1); + } + + static void rlinecurve (ENV &env, PARAM& param) + { + unsigned int arg_count = env.argStack.get_count (); + if (unlikely (arg_count < 8)) + return; + + unsigned int i = 0; + unsigned int line_limit = arg_count - 6; + for (; i + 2 <= line_limit; i += 2) + { + point_t pt1 = env.get_pt (); + pt1.move (env.eval_arg (i), env.eval_arg (i+1)); + PATH::line (env, param, pt1); + } + + point_t pt1 = env.get_pt (); + pt1.move (env.eval_arg (i), env.eval_arg (i+1)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (i+2), env.eval_arg (i+3)); + point_t pt3 = pt2; + pt3.move (env.eval_arg (i+4), env.eval_arg (i+5)); + PATH::curve (env, param, pt1, pt2, pt3); + } + + static void vvcurveto (ENV &env, PARAM& param) + { + unsigned int i = 0; + point_t pt1 = env.get_pt (); + if ((env.argStack.get_count () & 1) != 0) + pt1.move_x (env.eval_arg (i++)); + for (; i + 4 <= env.argStack.get_count (); i += 4) + { + pt1.move_y (env.eval_arg (i)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (i+1), env.eval_arg (i+2)); + point_t pt3 = pt2; + pt3.move_y (env.eval_arg (i+3)); + PATH::curve (env, param, pt1, pt2, pt3); + pt1 = env.get_pt (); + } + } + + static void hhcurveto (ENV &env, PARAM& param) + { + unsigned int i = 0; + point_t pt1 = env.get_pt (); + if ((env.argStack.get_count () & 1) != 0) + pt1.move_y (env.eval_arg (i++)); + for (; i + 4 <= env.argStack.get_count (); i += 4) + { + pt1.move_x (env.eval_arg (i)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (i+1), env.eval_arg (i+2)); + point_t pt3 = pt2; + pt3.move_x (env.eval_arg (i+3)); + PATH::curve (env, param, pt1, pt2, pt3); + pt1 = env.get_pt (); + } + } + + static void vhcurveto (ENV &env, PARAM& param) + { + point_t pt1, pt2, pt3; + unsigned int i = 0; + if ((env.argStack.get_count () % 8) >= 4) + { + point_t pt1 = env.get_pt (); + pt1.move_y (env.eval_arg (i)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (i+1), env.eval_arg (i+2)); + point_t pt3 = pt2; + pt3.move_x (env.eval_arg (i+3)); + i += 4; + + for (; i + 8 <= env.argStack.get_count (); i += 8) + { + PATH::curve (env, param, pt1, pt2, pt3); + pt1 = env.get_pt (); + pt1.move_x (env.eval_arg (i)); + pt2 = pt1; + pt2.move (env.eval_arg (i+1), env.eval_arg (i+2)); + pt3 = pt2; + pt3.move_y (env.eval_arg (i+3)); + PATH::curve (env, param, pt1, pt2, pt3); + + pt1 = pt3; + pt1.move_y (env.eval_arg (i+4)); + pt2 = pt1; + pt2.move (env.eval_arg (i+5), env.eval_arg (i+6)); + pt3 = pt2; + pt3.move_x (env.eval_arg (i+7)); + } + if (i < env.argStack.get_count ()) + pt3.move_y (env.eval_arg (i)); + PATH::curve (env, param, pt1, pt2, pt3); + } + else + { + for (; i + 8 <= env.argStack.get_count (); i += 8) + { + pt1 = env.get_pt (); + pt1.move_y (env.eval_arg (i)); + pt2 = pt1; + pt2.move (env.eval_arg (i+1), env.eval_arg (i+2)); + pt3 = pt2; + pt3.move_x (env.eval_arg (i+3)); + PATH::curve (env, param, pt1, pt2, pt3); + + pt1 = pt3; + pt1.move_x (env.eval_arg (i+4)); + pt2 = pt1; + pt2.move (env.eval_arg (i+5), env.eval_arg (i+6)); + pt3 = pt2; + pt3.move_y (env.eval_arg (i+7)); + if ((env.argStack.get_count () - i < 16) && ((env.argStack.get_count () & 1) != 0)) + pt3.move_x (env.eval_arg (i+8)); + PATH::curve (env, param, pt1, pt2, pt3); + } + } + } + + static void hvcurveto (ENV &env, PARAM& param) + { + point_t pt1, pt2, pt3; + unsigned int i = 0; + if ((env.argStack.get_count () % 8) >= 4) + { + point_t pt1 = env.get_pt (); + pt1.move_x (env.eval_arg (i)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (i+1), env.eval_arg (i+2)); + point_t pt3 = pt2; + pt3.move_y (env.eval_arg (i+3)); + i += 4; + + for (; i + 8 <= env.argStack.get_count (); i += 8) + { + PATH::curve (env, param, pt1, pt2, pt3); + pt1 = env.get_pt (); + pt1.move_y (env.eval_arg (i)); + pt2 = pt1; + pt2.move (env.eval_arg (i+1), env.eval_arg (i+2)); + pt3 = pt2; + pt3.move_x (env.eval_arg (i+3)); + PATH::curve (env, param, pt1, pt2, pt3); + + pt1 = pt3; + pt1.move_x (env.eval_arg (i+4)); + pt2 = pt1; + pt2.move (env.eval_arg (i+5), env.eval_arg (i+6)); + pt3 = pt2; + pt3.move_y (env.eval_arg (i+7)); + } + if (i < env.argStack.get_count ()) + pt3.move_x (env.eval_arg (i)); + PATH::curve (env, param, pt1, pt2, pt3); + } + else + { + for (; i + 8 <= env.argStack.get_count (); i += 8) + { + pt1 = env.get_pt (); + pt1.move_x (env.eval_arg (i)); + pt2 = pt1; + pt2.move (env.eval_arg (i+1), env.eval_arg (i+2)); + pt3 = pt2; + pt3.move_y (env.eval_arg (i+3)); + PATH::curve (env, param, pt1, pt2, pt3); + + pt1 = pt3; + pt1.move_y (env.eval_arg (i+4)); + pt2 = pt1; + pt2.move (env.eval_arg (i+5), env.eval_arg (i+6)); + pt3 = pt2; + pt3.move_x (env.eval_arg (i+7)); + if ((env.argStack.get_count () - i < 16) && ((env.argStack.get_count () & 1) != 0)) + pt3.move_y (env.eval_arg (i+8)); + PATH::curve (env, param, pt1, pt2, pt3); + } + } + } + + /* default actions to be overridden */ + static void moveto (ENV &env, PARAM& param, const point_t &pt) + { env.moveto (pt); } + + static void line (ENV &env, PARAM& param, const point_t &pt1) + { PATH::moveto (env, param, pt1); } + + static void curve (ENV &env, PARAM& param, const point_t &pt1, const point_t &pt2, const point_t &pt3) + { PATH::moveto (env, param, pt3); } + + static void hflex (ENV &env, PARAM& param) + { + if (likely (env.argStack.get_count () == 7)) + { + point_t pt1 = env.get_pt (); + pt1.move_x (env.eval_arg (0)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (1), env.eval_arg (2)); + point_t pt3 = pt2; + pt3.move_x (env.eval_arg (3)); + point_t pt4 = pt3; + pt4.move_x (env.eval_arg (4)); + point_t pt5 = pt4; + pt5.move_x (env.eval_arg (5)); + pt5.y = pt1.y; + point_t pt6 = pt5; + pt6.move_x (env.eval_arg (6)); + + curve2 (env, param, pt1, pt2, pt3, pt4, pt5, pt6); + } + else + env.set_error (); + } + + static void flex (ENV &env, PARAM& param) + { + if (likely (env.argStack.get_count () == 13)) + { + point_t pt1 = env.get_pt (); + pt1.move (env.eval_arg (0), env.eval_arg (1)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (2), env.eval_arg (3)); + point_t pt3 = pt2; + pt3.move (env.eval_arg (4), env.eval_arg (5)); + point_t pt4 = pt3; + pt4.move (env.eval_arg (6), env.eval_arg (7)); + point_t pt5 = pt4; + pt5.move (env.eval_arg (8), env.eval_arg (9)); + point_t pt6 = pt5; + pt6.move (env.eval_arg (10), env.eval_arg (11)); + + curve2 (env, param, pt1, pt2, pt3, pt4, pt5, pt6); + } + else + env.set_error (); + } + + static void hflex1 (ENV &env, PARAM& param) + { + if (likely (env.argStack.get_count () == 9)) + { + point_t pt1 = env.get_pt (); + pt1.move (env.eval_arg (0), env.eval_arg (1)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (2), env.eval_arg (3)); + point_t pt3 = pt2; + pt3.move_x (env.eval_arg (4)); + point_t pt4 = pt3; + pt4.move_x (env.eval_arg (5)); + point_t pt5 = pt4; + pt5.move (env.eval_arg (6), env.eval_arg (7)); + point_t pt6 = pt5; + pt6.move_x (env.eval_arg (8)); + pt6.y = env.get_pt ().y; + + curve2 (env, param, pt1, pt2, pt3, pt4, pt5, pt6); + } + else + env.set_error (); + } + + static void flex1 (ENV &env, PARAM& param) + { + if (likely (env.argStack.get_count () == 11)) + { + point_t d; + for (unsigned int i = 0; i < 10; i += 2) + d.move (env.eval_arg (i), env.eval_arg (i+1)); + + point_t pt1 = env.get_pt (); + pt1.move (env.eval_arg (0), env.eval_arg (1)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (2), env.eval_arg (3)); + point_t pt3 = pt2; + pt3.move (env.eval_arg (4), env.eval_arg (5)); + point_t pt4 = pt3; + pt4.move (env.eval_arg (6), env.eval_arg (7)); + point_t pt5 = pt4; + pt5.move (env.eval_arg (8), env.eval_arg (9)); + point_t pt6 = pt5; + + if (fabs (d.x.to_real ()) > fabs (d.y.to_real ())) + { + pt6.move_x (env.eval_arg (10)); + pt6.y = env.get_pt ().y; + } + else + { + pt6.x = env.get_pt ().x; + pt6.move_y (env.eval_arg (10)); + } + + curve2 (env, param, pt1, pt2, pt3, pt4, pt5, pt6); + } + else + env.set_error (); + } + + protected: + static void curve2 (ENV &env, PARAM& param, + const point_t &pt1, const point_t &pt2, const point_t &pt3, + const point_t &pt4, const point_t &pt5, const point_t &pt6) + { + PATH::curve (env, param, pt1, pt2, pt3); + PATH::curve (env, param, pt4, pt5, pt6); + } +}; + +template +struct cs_interpreter_t : interpreter_t +{ + cs_interpreter_t (ENV& env_) : interpreter_t (env_) {} + + bool interpret (PARAM& param) + { + SUPER::env.set_endchar (false); + + unsigned max_ops = HB_CFF_MAX_OPS; + for (;;) { + OPSET::process_op (SUPER::env.fetch_op (), SUPER::env, param); + if (unlikely (SUPER::env.in_error () || !--max_ops)) + { + SUPER::env.set_error (); + return false; + } + if (SUPER::env.is_endchar ()) + break; + } + + return true; + } + + private: + typedef interpreter_t SUPER; +}; + +} /* namespace CFF */ + +#endif /* HB_CFF_INTERP_CS_COMMON_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-cff-interp-dict-common.hh b/src/3rdparty/harfbuzz-ng/src/hb-cff-interp-dict-common.hh new file mode 100644 index 00000000000..53226b227e9 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-cff-interp-dict-common.hh @@ -0,0 +1,201 @@ +/* + * Copyright © 2018 Adobe 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. + * + * Adobe Author(s): Michiharu Ariza + */ +#ifndef HB_CFF_INTERP_DICT_COMMON_HH +#define HB_CFF_INTERP_DICT_COMMON_HH + +#include "hb-cff-interp-common.hh" + +namespace CFF { + +using namespace OT; + +/* an opstr and the parsed out dict value(s) */ +struct dict_val_t : op_str_t +{ + void init () {} + void fini () {} +}; + +typedef dict_val_t num_dict_val_t; + +template struct dict_values_t : parsed_values_t {}; + +template +struct top_dict_values_t : dict_values_t +{ + void init () + { + dict_values_t::init (); + charStringsOffset = 0; + FDArrayOffset = 0; + } + void fini () { dict_values_t::fini (); } + + unsigned int charStringsOffset; + unsigned int FDArrayOffset; +}; + +struct dict_opset_t : opset_t +{ + static void process_op (op_code_t op, interp_env_t& env) + { + switch (op) { + case OpCode_longintdict: /* 5-byte integer */ + env.argStack.push_longint_from_substr (env.str_ref); + break; + + case OpCode_BCD: /* real number */ + env.argStack.push_real (parse_bcd (env.str_ref)); + break; + + default: + opset_t::process_op (op, env); + break; + } + } + + /* Turns CFF's BCD format into strtod understandable string */ + static double parse_bcd (byte_str_ref_t& str_ref) + { + if (unlikely (str_ref.in_error ())) return .0; + + enum Nibble { DECIMAL=10, EXP_POS, EXP_NEG, RESERVED, NEG, END }; + + char buf[32]; + unsigned char byte = 0; + for (unsigned i = 0, count = 0; count < ARRAY_LENGTH (buf); ++i, ++count) + { + unsigned nibble; + if (!(i & 1)) + { + if (unlikely (!str_ref.avail ())) break; + + byte = str_ref[0]; + str_ref.inc (); + nibble = byte >> 4; + } + else + nibble = byte & 0x0F; + + if (unlikely (nibble == RESERVED)) break; + else if (nibble == END) + { + const char *p = buf; + double pv; + if (unlikely (!hb_parse_double (&p, p + count, &pv, true/* whole buffer */))) + break; + return pv; + } + else + { + buf[count] = "0123456789.EE?-?"[nibble]; + if (nibble == EXP_NEG) + { + ++count; + if (unlikely (count == ARRAY_LENGTH (buf))) break; + buf[count] = '-'; + } + } + } + + str_ref.set_error (); + return .0; + } + + static bool is_hint_op (op_code_t op) + { + switch (op) + { + case OpCode_BlueValues: + case OpCode_OtherBlues: + case OpCode_FamilyBlues: + case OpCode_FamilyOtherBlues: + case OpCode_StemSnapH: + case OpCode_StemSnapV: + case OpCode_StdHW: + case OpCode_StdVW: + case OpCode_BlueScale: + case OpCode_BlueShift: + case OpCode_BlueFuzz: + case OpCode_ForceBold: + case OpCode_LanguageGroup: + case OpCode_ExpansionFactor: + return true; + default: + return false; + } + } +}; + +template +struct top_dict_opset_t : dict_opset_t +{ + static void process_op (op_code_t op, interp_env_t& env, top_dict_values_t & dictval) + { + switch (op) { + case OpCode_CharStrings: + dictval.charStringsOffset = env.argStack.pop_uint (); + env.clear_args (); + break; + case OpCode_FDArray: + dictval.FDArrayOffset = env.argStack.pop_uint (); + env.clear_args (); + break; + case OpCode_FontMatrix: + env.clear_args (); + break; + default: + dict_opset_t::process_op (op, env); + break; + } + } +}; + +template +struct dict_interpreter_t : interpreter_t +{ + dict_interpreter_t (ENV& env_) : interpreter_t (env_) {} + + bool interpret (PARAM& param) + { + param.init (); + while (SUPER::env.str_ref.avail ()) + { + OPSET::process_op (SUPER::env.fetch_op (), SUPER::env, param); + if (unlikely (SUPER::env.in_error ())) + return false; + } + + return true; + } + + private: + typedef interpreter_t SUPER; +}; + +} /* namespace CFF */ + +#endif /* HB_CFF_INTERP_DICT_COMMON_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-cff1-interp-cs.hh b/src/3rdparty/harfbuzz-ng/src/hb-cff1-interp-cs.hh new file mode 100644 index 00000000000..d8868efa53f --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-cff1-interp-cs.hh @@ -0,0 +1,160 @@ +/* + * Copyright © 2018 Adobe 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. + * + * Adobe Author(s): Michiharu Ariza + */ +#ifndef HB_CFF1_INTERP_CS_HH +#define HB_CFF1_INTERP_CS_HH + +#include "hb.hh" +#include "hb-cff-interp-cs-common.hh" + +namespace CFF { + +using namespace OT; + +typedef biased_subrs_t cff1_biased_subrs_t; + +struct cff1_cs_interp_env_t : cs_interp_env_t +{ + template + cff1_cs_interp_env_t (const hb_ubytes_t &str, ACC &acc, unsigned int fd, + const int *coords_=nullptr, unsigned int num_coords_=0) + : SUPER (str, acc.globalSubrs, acc.privateDicts[fd].localSubrs) + { + processed_width = false; + has_width = false; + arg_start = 0; + in_seac = false; + } + + void set_width (bool has_width_) + { + if (likely (!processed_width && (SUPER::argStack.get_count () > 0))) + { + if (has_width_) + { + width = SUPER::argStack[0]; + has_width = true; + arg_start = 1; + } + } + processed_width = true; + } + + void clear_args () + { + arg_start = 0; + SUPER::clear_args (); + } + + void set_in_seac (bool _in_seac) { in_seac = _in_seac; } + + bool processed_width; + bool has_width; + unsigned int arg_start; + number_t width; + bool in_seac; + + private: + typedef cs_interp_env_t SUPER; +}; + +template > +struct cff1_cs_opset_t : cs_opset_t +{ + /* PostScript-originated legacy opcodes (OpCode_add etc) are unsupported */ + /* Type 1-originated deprecated opcodes, seac behavior of endchar and dotsection are supported */ + + static void process_op (op_code_t op, cff1_cs_interp_env_t &env, PARAM& param) + { + switch (op) { + case OpCode_dotsection: + SUPER::flush_args_and_op (op, env, param); + break; + + case OpCode_endchar: + OPSET::check_width (op, env, param); + if (env.argStack.get_count () >= 4) + { + OPSET::process_seac (env, param); + } + OPSET::flush_args_and_op (op, env, param); + env.set_endchar (true); + break; + + default: + SUPER::process_op (op, env, param); + } + } + + static void check_width (op_code_t op, cff1_cs_interp_env_t &env, PARAM& param) + { + if (!env.processed_width) + { + bool has_width = false; + switch (op) + { + case OpCode_endchar: + case OpCode_hstem: + case OpCode_hstemhm: + case OpCode_vstem: + case OpCode_vstemhm: + case OpCode_hintmask: + case OpCode_cntrmask: + has_width = ((env.argStack.get_count () & 1) != 0); + break; + case OpCode_hmoveto: + case OpCode_vmoveto: + has_width = (env.argStack.get_count () > 1); + break; + case OpCode_rmoveto: + has_width = (env.argStack.get_count () > 2); + break; + default: + return; + } + env.set_width (has_width); + } + } + + static void process_seac (cff1_cs_interp_env_t &env, PARAM& param) + { + } + + static void flush_args (cff1_cs_interp_env_t &env, PARAM& param) + { + SUPER::flush_args (env, param); + env.clear_args (); /* pop off width */ + } + + private: + typedef cs_opset_t SUPER; +}; + +template +using cff1_cs_interpreter_t = cs_interpreter_t; + +} /* namespace CFF */ + +#endif /* HB_CFF1_INTERP_CS_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-cff2-interp-cs.hh b/src/3rdparty/harfbuzz-ng/src/hb-cff2-interp-cs.hh new file mode 100644 index 00000000000..915b10cf39a --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-cff2-interp-cs.hh @@ -0,0 +1,282 @@ +/* + * Copyright © 2018 Adobe 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. + * + * Adobe Author(s): Michiharu Ariza + */ +#ifndef HB_CFF2_INTERP_CS_HH +#define HB_CFF2_INTERP_CS_HH + +#include "hb.hh" +#include "hb-cff-interp-cs-common.hh" + +namespace CFF { + +using namespace OT; + +struct blend_arg_t : number_t +{ + void set_int (int v) { reset_blends (); number_t::set_int (v); } + void set_fixed (int32_t v) { reset_blends (); number_t::set_fixed (v); } + void set_real (double v) { reset_blends (); number_t::set_real (v); } + + void set_blends (unsigned int numValues_, unsigned int valueIndex_, + hb_array_t blends_) + { + numValues = numValues_; + valueIndex = valueIndex_; + unsigned numBlends = blends_.length; + if (unlikely (!deltas.resize_exact (numBlends))) + return; + for (unsigned int i = 0; i < numBlends; i++) + deltas.arrayZ[i] = blends_.arrayZ[i]; + } + + bool blending () const { return deltas.length > 0; } + void reset_blends () + { + numValues = valueIndex = 0; + deltas.shrink (0); + } + + unsigned int numValues; + unsigned int valueIndex; + hb_vector_t deltas; +}; + +typedef biased_subrs_t cff2_biased_subrs_t; + +template +struct cff2_cs_interp_env_t : cs_interp_env_t +{ + template + cff2_cs_interp_env_t (const hb_ubytes_t &str, ACC &acc, unsigned int fd, + const int *coords_=nullptr, unsigned int num_coords_=0) + : SUPER (str, acc.globalSubrs, acc.privateDicts[fd].localSubrs) + { + coords = coords_; + num_coords = num_coords_; + varStore = acc.varStore; + seen_blend = false; + seen_vsindex_ = false; + scalars.init (); + do_blend = num_coords && coords && varStore->size; + set_ivs (acc.privateDicts[fd].ivs); + } + + void fini () + { + scalars.fini (); + SUPER::fini (); + } + + op_code_t fetch_op () + { + if (this->str_ref.avail ()) + return SUPER::fetch_op (); + + /* make up return or endchar op */ + if (this->callStack.is_empty ()) + return OpCode_endchar; + else + return OpCode_return; + } + + const ELEM& eval_arg (unsigned int i) + { + return SUPER::argStack[i]; + } + + const ELEM& pop_arg () + { + return SUPER::argStack.pop (); + } + + void process_blend () + { + if (!seen_blend) + { + region_count = varStore->varStore.get_region_index_count (get_ivs ()); + if (do_blend) + { + if (unlikely (!scalars.resize_exact (region_count))) + SUPER::set_error (); + else + varStore->varStore.get_region_scalars (get_ivs (), coords, num_coords, + &scalars[0], region_count); + } + seen_blend = true; + } + } + + void process_vsindex () + { + unsigned int index = SUPER::argStack.pop_uint (); + if (unlikely (seen_vsindex () || seen_blend)) + { + SUPER::set_error (); + } + else + { + set_ivs (index); + } + seen_vsindex_ = true; + } + + unsigned int get_region_count () const { return region_count; } + void set_region_count (unsigned int region_count_) { region_count = region_count_; } + unsigned int get_ivs () const { return ivs; } + void set_ivs (unsigned int ivs_) { ivs = ivs_; } + bool seen_vsindex () const { return seen_vsindex_; } + + double blend_deltas (hb_array_t deltas) const + { + double v = 0; + if (do_blend) + { + if (likely (scalars.length == deltas.length)) + { + unsigned count = scalars.length; + for (unsigned i = 0; i < count; i++) + v += (double) scalars.arrayZ[i] * deltas.arrayZ[i].to_real (); + } + } + return v; + } + + bool have_coords () const { return num_coords; } + + protected: + const int *coords; + unsigned int num_coords; + const CFF2VariationStore *varStore; + unsigned int region_count; + unsigned int ivs; + hb_vector_t scalars; + bool do_blend; + bool seen_vsindex_; + bool seen_blend; + + typedef cs_interp_env_t SUPER; +}; +template , PARAM>> +struct cff2_cs_opset_t : cs_opset_t, PARAM, PATH> +{ + static void process_op (op_code_t op, cff2_cs_interp_env_t &env, PARAM& param) + { + switch (op) { + case OpCode_callsubr: + case OpCode_callgsubr: + /* a subroutine number shouldn't be a blended value */ +#if 0 + if (unlikely (env.argStack.peek ().blending ())) + { + env.set_error (); + break; + } +#endif + SUPER::process_op (op, env, param); + break; + + case OpCode_blendcs: + OPSET::process_blend (env, param); + break; + + case OpCode_vsindexcs: +#if 0 + if (unlikely (env.argStack.peek ().blending ())) + { + env.set_error (); + break; + } +#endif + OPSET::process_vsindex (env, param); + break; + + default: + SUPER::process_op (op, env, param); + } + } + + template + static void process_arg_blend (cff2_cs_interp_env_t &env, + ELEM &arg, + const hb_array_t blends, + unsigned n, unsigned i) + { + if (env.have_coords ()) + arg.set_int (round (arg.to_real () + env.blend_deltas (blends))); + else + arg.set_blends (n, i, blends); + } + template + static void process_arg_blend (cff2_cs_interp_env_t &env, + ELEM &arg, + const hb_array_t blends, + unsigned n, unsigned i) + { + arg.set_real (arg.to_real () + env.blend_deltas (blends)); + } + + static void process_blend (cff2_cs_interp_env_t &env, PARAM& param) + { + unsigned int n, k; + + env.process_blend (); + k = env.get_region_count (); + n = env.argStack.pop_uint (); + /* copy the blend values into blend array of the default values */ + unsigned int start = env.argStack.get_count () - ((k+1) * n); + /* let an obvious error case fail, but note CFF2 spec doesn't forbid n==0 */ + if (unlikely (start > env.argStack.get_count ())) + { + env.set_error (); + return; + } + for (unsigned int i = 0; i < n; i++) + { + const hb_array_t blends = env.argStack.sub_array (start + n + (i * k), k); + process_arg_blend (env, env.argStack[start + i], blends, n, i); + } + + /* pop off blend values leaving default values now adorned with blend values */ + env.argStack.pop (k * n); + } + + static void process_vsindex (cff2_cs_interp_env_t &env, PARAM& param) + { + env.process_vsindex (); + env.clear_args (); + } + + private: + typedef cs_opset_t, PARAM, PATH> SUPER; +}; + +template +using cff2_cs_interpreter_t = cs_interpreter_t, OPSET, PARAM>; + +} /* namespace CFF */ + +#endif /* HB_CFF2_INTERP_CS_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-common.cc b/src/3rdparty/harfbuzz-ng/src/hb-common.cc index cb1fb43ffbb..0c13c7d171f 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-common.cc +++ b/src/3rdparty/harfbuzz-ng/src/hb-common.cc @@ -26,33 +26,54 @@ * Google Author(s): Behdad Esfahbod */ -#include "hb-private.hh" +#include "hb.hh" +#include "hb-machinery.hh" -#include "hb-mutex-private.hh" -#include "hb-object-private.hh" -#include -#ifdef HAVE_XLOCALE_H -#include -#endif +/** + * SECTION:hb-common + * @title: hb-common + * @short_description: Common data types + * @include: hb.h + * + * Common data types used across HarfBuzz are defined here. + **/ /* hb_options_t */ -hb_options_union_t _hb_options; +hb_atomic_int_t _hb_options; void -_hb_options_init (void) +_hb_options_init () { hb_options_union_t u; u.i = 0; - u.opts.initialized = 1; + u.opts.initialized = true; - char *c = getenv ("HB_OPTIONS"); - u.opts.uniscribe_bug_compatible = c && strstr (c, "uniscribe-bug-compatible"); + const char *c = getenv ("HB_OPTIONS"); + if (c) + { + while (*c) + { + const char *p = strchr (c, ':'); + if (!p) + p = c + strlen (c); + +#define OPTION(name, symbol) \ + if (0 == strncmp (c, name, p - c) && strlen (name) == static_cast(p - c)) do { u.opts.symbol = true; } while (0) + + OPTION ("uniscribe-bug-compatible", uniscribe_bug_compatible); + +#undef OPTION + + c = *p ? p + 1 : p; + } + + } /* This is idempotent and threadsafe. */ - _hb_options = u; + _hb_options = u.i; } @@ -60,12 +81,15 @@ _hb_options_init (void) /** * hb_tag_from_string: - * @str: (array length=len) (element-type uint8_t): - * @len: + * @str: (array length=len) (element-type uint8_t): String to convert + * @len: Length of @str, or -1 if it is `NULL`-terminated * - * + * Converts a string into an #hb_tag_t. Valid tags + * are four characters. Shorter input strings will be + * padded with spaces. Longer input strings will be + * truncated. * - * Return value: + * Return value: The #hb_tag_t corresponding to @str * * Since: 0.9.2 **/ @@ -90,10 +114,11 @@ hb_tag_from_string (const char *str, int len) /** * hb_tag_to_string: - * @tag: - * @buf: (out caller-allocates) (array fixed-size=4) (element-type uint8_t): + * @tag: #hb_tag_t to convert + * @buf: (out caller-allocates) (array fixed-size=4) (element-type uint8_t): Converted string * - * + * Converts an #hb_tag_t to a string and returns it in @buf. + * Strings will be four characters long. * * Since: 0.9.5 **/ @@ -109,7 +134,7 @@ hb_tag_to_string (hb_tag_t tag, char *buf) /* hb_direction_t */ -const char direction_strings[][4] = { +static const char direction_strings[][4] = { "ltr", "rtl", "ttb", @@ -118,12 +143,17 @@ const char direction_strings[][4] = { /** * hb_direction_from_string: - * @str: (array length=len) (element-type uint8_t): - * @len: + * @str: (array length=len) (element-type uint8_t): String to convert + * @len: Length of @str, or -1 if it is `NULL`-terminated * - * + * Converts a string to an #hb_direction_t. * - * Return value: + * Matching is loose and applies only to the first letter. For + * examples, "LTR" and "left-to-right" will both return #HB_DIRECTION_LTR. + * + * Unmatched strings will return #HB_DIRECTION_INVALID. + * + * Return value: The #hb_direction_t matching @str * * Since: 0.9.2 **/ @@ -146,11 +176,11 @@ hb_direction_from_string (const char *str, int len) /** * hb_direction_to_string: - * @direction: + * @direction: The #hb_direction_t to convert * - * + * Converts an #hb_direction_t to a string. * - * Return value: (transfer none): + * Return value: (transfer none): The string corresponding to @direction * * Since: 0.9.2 **/ @@ -176,7 +206,7 @@ static const char canon_map[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0, '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0, - '-', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, '-', 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, 0 @@ -219,20 +249,17 @@ struct hb_language_item_t { struct hb_language_item_t *next; hb_language_t lang; - inline bool operator == (const char *s) const { - return lang_equal (lang, s); - } + bool operator == (const char *s) const + { return lang_equal (lang, s); } - inline hb_language_item_t & operator = (const char *s) { - /* If a custom allocated is used calling strdup() pairs - badly with a call to the custom free() in finish() below. - Therefore don't call strdup(), implement its behavior. - */ + hb_language_item_t & operator = (const char *s) + { + /* We can't call strdup(), because we allow custom allocators. */ size_t len = strlen(s) + 1; - lang = (hb_language_t) malloc(len); + lang = (hb_language_t) hb_malloc(len); if (likely (lang)) { - memcpy((unsigned char *) lang, s, len); + hb_memcpy((unsigned char *) lang, s, len); for (unsigned char *p = (unsigned char *) lang; *p; p++) *p = canon_map[*p]; } @@ -240,59 +267,61 @@ struct hb_language_item_t { return *this; } - void finish (void) { free ((void *) lang); } + void fini () { hb_free ((void *) lang); } }; -/* Thread-safe lock-free language list */ +/* Thread-safe lockfree language list */ -static hb_language_item_t *langs; +static hb_atomic_ptr_t langs; -#ifdef HB_USE_ATEXIT -static void -free_langs (void) +static inline void +free_langs () { - while (langs) { - hb_language_item_t *next = langs->next; - langs->finish (); - free (langs); - langs = next; +retry: + hb_language_item_t *first_lang = langs; + if (unlikely (!langs.cmpexch (first_lang, nullptr))) + goto retry; + + while (first_lang) { + hb_language_item_t *next = first_lang->next; + first_lang->fini (); + hb_free (first_lang); + first_lang = next; } } -#endif static hb_language_item_t * lang_find_or_insert (const char *key) { retry: - hb_language_item_t *first_lang = (hb_language_item_t *) hb_atomic_ptr_get (&langs); + hb_language_item_t *first_lang = langs; for (hb_language_item_t *lang = first_lang; lang; lang = lang->next) if (*lang == key) return lang; /* Not found; allocate one. */ - hb_language_item_t *lang = (hb_language_item_t *) calloc (1, sizeof (hb_language_item_t)); + hb_language_item_t *lang = (hb_language_item_t *) hb_calloc (1, sizeof (hb_language_item_t)); if (unlikely (!lang)) return nullptr; lang->next = first_lang; *lang = key; if (unlikely (!lang->lang)) { - free (lang); + hb_free (lang); return nullptr; } - if (!hb_atomic_ptr_cmpexch (&langs, first_lang, lang)) { - lang->finish (); - free (lang); + if (unlikely (!langs.cmpexch (first_lang, lang))) + { + lang->fini (); + hb_free (lang); goto retry; } -#ifdef HB_USE_ATEXIT if (!first_lang) - atexit (free_langs); /* First person registers atexit() callback. */ -#endif + hb_atexit (free_langs); /* First person registers atexit() callback. */ return lang; } @@ -301,14 +330,14 @@ retry: /** * hb_language_from_string: * @str: (array length=len) (element-type uint8_t): a string representing - * ISO 639 language code - * @len: length of the @str, or -1 if it is %NULL-terminated. + * a BCP 47 language tag + * @len: length of the @str, or -1 if it is `NULL`-terminated. * - * Converts @str representing an ISO 639 language code to the corresponding + * Converts @str representing a BCP 47 language tag to the corresponding * #hb_language_t. * * Return value: (transfer none): - * The #hb_language_t corresponding to the ISO 639 language code. + * The #hb_language_t corresponding to the BCP 47 language tag. * * Since: 0.9.2 **/ @@ -323,8 +352,8 @@ hb_language_from_string (const char *str, int len) { /* NUL-terminate it. */ char strbuf[64]; - len = MIN (len, (int) sizeof (strbuf) - 1); - memcpy (strbuf, str, len); + len = hb_min (len, (int) sizeof (strbuf) - 1); + hb_memcpy (strbuf, str, len); strbuf[len] = '\0'; item = lang_find_or_insert (strbuf); } @@ -336,12 +365,12 @@ hb_language_from_string (const char *str, int len) /** * hb_language_to_string: - * @language: an #hb_language_t to convert. + * @language: The #hb_language_t to convert * - * See hb_language_from_string(). + * Converts an #hb_language_t to a string. * * Return value: (transfer none): - * A %NULL-terminated string representing the @language. Must not be freed by + * A `NULL`-terminated string representing the @language. Must not be freed by * the caller. * * Since: 0.9.2 @@ -349,31 +378,73 @@ hb_language_from_string (const char *str, int len) const char * hb_language_to_string (hb_language_t language) { - /* This is actually nullptr-safe! */ + if (unlikely (!language)) return nullptr; + return language->s; } /** * hb_language_get_default: * - * + * Fetch the default language from current locale. * - * Return value: (transfer none): + * Note that the first time this function is called, it calls + * "setlocale (LC_CTYPE, nullptr)" to fetch current locale. The underlying + * setlocale function is, in many implementations, NOT threadsafe. To avoid + * problems, call this function once before multiple threads can call it. + * This function is only used from hb_buffer_guess_segment_properties() by + * HarfBuzz itself. + * + * Return value: (transfer none): The default language of the locale as + * an #hb_language_t * * Since: 0.9.2 **/ hb_language_t -hb_language_get_default (void) +hb_language_get_default () { - static hb_language_t default_language = HB_LANGUAGE_INVALID; + static hb_atomic_ptr_t default_language; - hb_language_t language = (hb_language_t) hb_atomic_ptr_get (&default_language); - if (unlikely (language == HB_LANGUAGE_INVALID)) { - language = hb_language_from_string (setlocale (LC_CTYPE, nullptr), -1); - (void) hb_atomic_ptr_cmpexch (&default_language, HB_LANGUAGE_INVALID, language); + hb_language_t language = default_language; + if (unlikely (language == HB_LANGUAGE_INVALID)) + { + language = hb_language_from_string (hb_setlocale (LC_CTYPE, nullptr), -1); + (void) default_language.cmpexch (HB_LANGUAGE_INVALID, language); } - return default_language; + return language; +} + +/** + * hb_language_matches: + * @language: The #hb_language_t to work on + * @specific: Another #hb_language_t + * + * Check whether a second language tag is the same or a more + * specific version of the provided language tag. For example, + * "fa_IR.utf8" is a more specific tag for "fa" or for "fa_IR". + * + * Return value: `true` if languages match, `false` otherwise. + * + * Since: 5.0.0 + **/ +hb_bool_t +hb_language_matches (hb_language_t language, + hb_language_t specific) +{ + if (language == specific) return true; + if (!language || !specific) return false; + + const char *l = language->s; + const char *s = specific->s; + unsigned ll = strlen (l); + unsigned sl = strlen (s); + + if (ll > sl) + return false; + + return strncmp (l, s, ll) == 0 && + (s[ll] == '\0' || s[ll] == '-'); } @@ -381,12 +452,12 @@ hb_language_get_default (void) /** * hb_script_from_iso15924_tag: - * @tag: an #hb_tag_t representing an ISO 15924 tag. + * @tag: an #hb_tag_t representing an ISO 15924 tag. * - * Converts an ISO 15924 script tag to a corresponding #hb_script_t. + * Converts an ISO 15924 script tag to a corresponding #hb_script_t. * - * Return value: - * An #hb_script_t corresponding to the ISO 15924 tag. + * Return value: + * An #hb_script_t corresponding to the ISO 15924 tag. * * Since: 0.9.2 **/ @@ -407,8 +478,13 @@ hb_script_from_iso15924_tag (hb_tag_t tag) case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED; case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC; - /* Script variants from http://unicode.org/iso15924/ */ + /* Script variants from https://unicode.org/iso15924/ */ + case HB_TAG('A','r','a','n'): return HB_SCRIPT_ARABIC; case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC; + case HB_TAG('G','e','o','k'): return HB_SCRIPT_GEORGIAN; + case HB_TAG('H','a','n','s'): return HB_SCRIPT_HAN; + case HB_TAG('H','a','n','t'): return HB_SCRIPT_HAN; + case HB_TAG('J','a','m','o'): return HB_SCRIPT_HANGUL; case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN; case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN; case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC; @@ -427,15 +503,15 @@ hb_script_from_iso15924_tag (hb_tag_t tag) /** * hb_script_from_string: * @str: (array length=len) (element-type uint8_t): a string representing an - * ISO 15924 tag. - * @len: length of the @str, or -1 if it is %NULL-terminated. + * ISO 15924 tag. + * @len: length of the @str, or -1 if it is `NULL`-terminated. * - * Converts a string @str representing an ISO 15924 script tag to a + * Converts a string @str representing an ISO 15924 script tag to a * corresponding #hb_script_t. Shorthand for hb_tag_from_string() then * hb_script_from_iso15924_tag(). * - * Return value: - * An #hb_script_t corresponding to the ISO 15924 tag. + * Return value: + * An #hb_script_t corresponding to the ISO 15924 tag. * * Since: 0.9.2 **/ @@ -447,12 +523,12 @@ hb_script_from_string (const char *str, int len) /** * hb_script_to_iso15924_tag: - * @script: an #hb_script_ to convert. + * @script: an #hb_script_t to convert. * - * See hb_script_from_iso15924_tag(). + * Converts an #hb_script_t to a corresponding ISO 15924 script tag. * * Return value: - * An #hb_tag_t representing an ISO 15924 script tag. + * An #hb_tag_t representing an ISO 15924 script tag. * * Since: 0.9.2 **/ @@ -464,18 +540,23 @@ hb_script_to_iso15924_tag (hb_script_t script) /** * hb_script_get_horizontal_direction: - * @script: + * @script: The #hb_script_t to query * - * + * Fetches the #hb_direction_t of a script when it is + * set horizontally. All right-to-left scripts will return + * #HB_DIRECTION_RTL. All left-to-right scripts will return + * #HB_DIRECTION_LTR. Scripts that can be written either + * horizontally or vertically will return #HB_DIRECTION_INVALID. + * Unknown scripts will return #HB_DIRECTION_LTR. * - * Return value: + * Return value: The horizontal #hb_direction_t of @script * * Since: 0.9.2 **/ hb_direction_t hb_script_get_horizontal_direction (hb_script_t script) { - /* http://goo.gl/x9ilM */ + /* https://docs.google.com/spreadsheets/d/1Y90M0Ie3MUJ6UVCRDOypOtijlMDLNNyyLk36T6iMu0o */ switch ((hb_tag_t) script) { /* Unicode-1.1 additions */ @@ -524,57 +605,62 @@ hb_script_get_horizontal_direction (hb_script_t script) case HB_SCRIPT_PSALTER_PAHLAVI: /* Unicode-8.0 additions */ - case HB_SCRIPT_OLD_HUNGARIAN: + case HB_SCRIPT_HATRAN: /* Unicode-9.0 additions */ case HB_SCRIPT_ADLAM: + /* Unicode-11.0 additions */ + case HB_SCRIPT_HANIFI_ROHINGYA: + case HB_SCRIPT_OLD_SOGDIAN: + case HB_SCRIPT_SOGDIAN: + + /* Unicode-12.0 additions */ + case HB_SCRIPT_ELYMAIC: + + /* Unicode-13.0 additions */ + case HB_SCRIPT_CHORASMIAN: + case HB_SCRIPT_YEZIDI: + + /* Unicode-14.0 additions */ + case HB_SCRIPT_OLD_UYGHUR: + return HB_DIRECTION_RTL; + + + /* https://github.com/harfbuzz/harfbuzz/issues/1000 */ + case HB_SCRIPT_OLD_HUNGARIAN: + case HB_SCRIPT_OLD_ITALIC: + case HB_SCRIPT_RUNIC: + case HB_SCRIPT_TIFINAGH: + + return HB_DIRECTION_INVALID; } return HB_DIRECTION_LTR; } -/* hb_user_data_array_t */ - -bool -hb_user_data_array_t::set (hb_user_data_key_t *key, - void * data, - hb_destroy_func_t destroy, - hb_bool_t replace) -{ - if (!key) - return false; - - if (replace) { - if (!data && !destroy) { - items.remove (key, lock); - return true; - } - } - hb_user_data_item_t item = {key, data, destroy}; - bool ret = !!items.replace_or_insert (item, lock, (bool) replace); - - return ret; -} - -void * -hb_user_data_array_t::get (hb_user_data_key_t *key) -{ - hb_user_data_item_t item = {nullptr, nullptr, nullptr}; - - return items.find (key, &item, lock) ? item.data : nullptr; -} - - /* hb_version */ + +/** + * SECTION:hb-version + * @title: hb-version + * @short_description: Information about the version of HarfBuzz in use + * @include: hb.h + * + * These functions and macros allow accessing version of the HarfBuzz + * library used at compile- as well as run-time, and to direct code + * conditionally based on those versions, again, at compile- or run-time. + **/ + + /** * hb_version: - * @major: (out): Library major version component. - * @minor: (out): Library minor version component. - * @micro: (out): Library micro version component. + * @major: (out): Library major version component + * @minor: (out): Library minor version component + * @micro: (out): Library micro version component * * Returns library version as three integer components. * @@ -595,25 +681,27 @@ hb_version (unsigned int *major, * * Returns library version as a string with three components. * - * Return value: library version string. + * Return value: Library version string * * Since: 0.9.2 **/ const char * -hb_version_string (void) +hb_version_string () { return HB_VERSION_STRING; } /** * hb_version_atleast: - * @major: - * @minor: - * @micro: + * @major: Library major version component + * @minor: Library minor version component + * @micro: Library micro version component * - * + * Tests the library version against a minimum value, + * as three integer components. * - * Return value: + * Return value: `true` if the library is equal to or greater than + * the test value, `false` otherwise * * Since: 0.9.30 **/ @@ -652,125 +740,24 @@ parse_char (const char **pp, const char *end, char c) static bool parse_uint (const char **pp, const char *end, unsigned int *pv) { - char buf[32]; - unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp)); - strncpy (buf, *pp, len); - buf[len] = '\0'; - - char *p = buf; - char *pend = p; - unsigned int v; - - /* Intentionally use strtol instead of strtoul, such that - * -1 turns into "big number"... */ - errno = 0; - v = strtol (p, &pend, 0); - if (errno || p == pend) - return false; + /* Intentionally use hb_parse_int inside instead of hb_parse_uint, + * such that -1 turns into "big number"... */ + int v; + if (unlikely (!hb_parse_int (pp, end, &v))) return false; *pv = v; - *pp += pend - p; return true; } static bool parse_uint32 (const char **pp, const char *end, uint32_t *pv) { - char buf[32]; - unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp)); - strncpy (buf, *pp, len); - buf[len] = '\0'; - - char *p = buf; - char *pend = p; - unsigned int v; - - /* Intentionally use strtol instead of strtoul, such that - * -1 turns into "big number"... */ - errno = 0; - v = strtol (p, &pend, 0); - if (errno || p == pend) - return false; + /* Intentionally use hb_parse_int inside instead of hb_parse_uint, + * such that -1 turns into "big number"... */ + int v; + if (unlikely (!hb_parse_int (pp, end, &v))) return false; *pv = v; - *pp += pend - p; - return true; -} - -#if defined (HAVE_NEWLOCALE) && defined (HAVE_STRTOD_L) -#define USE_XLOCALE 1 -#define HB_LOCALE_T locale_t -#define HB_CREATE_LOCALE(locName) newlocale (LC_ALL_MASK, locName, nullptr) -#define HB_FREE_LOCALE(loc) freelocale (loc) -#elif defined(_MSC_VER) -#define USE_XLOCALE 1 -#define HB_LOCALE_T _locale_t -#define HB_CREATE_LOCALE(locName) _create_locale (LC_ALL, locName) -#define HB_FREE_LOCALE(loc) _free_locale (loc) -#define strtod_l(a, b, c) _strtod_l ((a), (b), (c)) -#endif - -#ifdef USE_XLOCALE - -static HB_LOCALE_T C_locale; - -#ifdef HB_USE_ATEXIT -static void -free_C_locale (void) -{ - if (C_locale) - HB_FREE_LOCALE (C_locale); -} -#endif - -static HB_LOCALE_T -get_C_locale (void) -{ -retry: - HB_LOCALE_T C = (HB_LOCALE_T) hb_atomic_ptr_get (&C_locale); - - if (unlikely (!C)) - { - C = HB_CREATE_LOCALE ("C"); - - if (!hb_atomic_ptr_cmpexch (&C_locale, nullptr, C)) - { - HB_FREE_LOCALE (C_locale); - goto retry; - } - -#ifdef HB_USE_ATEXIT - atexit (free_C_locale); /* First person registers atexit() callback. */ -#endif - } - - return C; -} -#endif - -static bool -parse_float (const char **pp, const char *end, float *pv) -{ - char buf[32]; - unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp)); - strncpy (buf, *pp, len); - buf[len] = '\0'; - - char *p = buf; - char *pend = p; - float v; - - errno = 0; -#ifdef USE_XLOCALE - v = strtod_l (p, &pend, get_C_locale ()); -#else - v = strtod (p, &pend); -#endif - if (errno || p == pend) - return false; - - *pv = v; - *pp += pend - p; return true; } @@ -784,9 +771,14 @@ parse_bool (const char **pp, const char *end, uint32_t *pv) (*pp)++; /* CSS allows on/off as aliases 1/0. */ - if (*pp - p == 2 && 0 == strncmp (p, "on", 2)) + if (*pp - p == 2 + && TOLOWER (p[0]) == 'o' + && TOLOWER (p[1]) == 'n') *pv = 1; - else if (*pp - p == 3 && 0 == strncmp (p, "off", 3)) + else if (*pp - p == 3 + && TOLOWER (p[0]) == 'o' + && TOLOWER (p[1]) == 'f' + && TOLOWER (p[2]) == 'f') *pv = 0; else return false; @@ -823,7 +815,7 @@ parse_tag (const char **pp, const char *end, hb_tag_t *tag) } const char *p = *pp; - while (*pp < end && ISALNUM(**pp)) + while (*pp < end && (**pp != ' ' && **pp != '=' && **pp != '[' && **pp != quote)) (*pp)++; if (p == *pp || *pp - p > 4) @@ -852,15 +844,15 @@ parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature) bool has_start; - feature->start = 0; - feature->end = (unsigned int) -1; + feature->start = HB_FEATURE_GLOBAL_START; + feature->end = HB_FEATURE_GLOBAL_END; if (!parse_char (pp, end, '[')) return true; has_start = parse_uint (pp, end, &feature->start); - if (parse_char (pp, end, ':')) { + if (parse_char (pp, end, ':') || parse_char (pp, end, ';')) { parse_uint (pp, end, &feature->end); } else { if (has_start) @@ -875,10 +867,10 @@ parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *fea { bool had_equal = parse_char (pp, end, '='); bool had_value = parse_uint32 (pp, end, &feature->value) || - parse_bool (pp, end, &feature->value); + parse_bool (pp, end, &feature->value); /* CSS doesn't use equal-sign between tag and value. * If there was an equal-sign, then there *must* be a value. - * A value without an eqaul-sign is ok, but not required. */ + * A value without an equal-sign is ok, but not required. */ return !had_equal || had_value; } @@ -896,15 +888,49 @@ parse_one_feature (const char **pp, const char *end, hb_feature_t *feature) /** * hb_feature_from_string: * @str: (array length=len) (element-type uint8_t): a string to parse - * @len: length of @str, or -1 if string is %NULL terminated + * @len: length of @str, or -1 if string is `NULL` terminated * @feature: (out): the #hb_feature_t to initialize with the parsed values * * Parses a string into a #hb_feature_t. * - * TODO: document the syntax here. + * The format for specifying feature strings follows. All valid CSS + * font-feature-settings values other than 'normal' and the global values are + * also accepted, though not documented below. CSS string escapes are not + * supported. + * + * The range indices refer to the positions between Unicode characters. The + * position before the first character is always 0. + * + * The format is Python-esque. Here is how it all works: + * + * + * + * + * Syntax Value Start End + * + * + * Setting value: + * kern 1 0 Turn feature on + * +kern 1 0 Turn feature on + * -kern 0 0 Turn feature off + * kern=0 0 0 Turn feature off + * kern=1 1 0 Turn feature on + * aalt=2 2 0 Choose 2nd alternate + * Setting index: + * kern[] 1 0 Turn feature on + * kern[:] 1 0 Turn feature on + * kern[5:] 1 5 Turn feature on, partial + * kern[:5] 1 0 5 Turn feature on, partial + * kern[3:5] 1 3 5 Turn feature on, range + * kern[3] 1 3 3+1 Turn feature on, single char + * Mixing it all: + * aalt[3:5]=2 2 3 5 Turn 2nd alternate on for range + * + * + * * * Return value: - * %true if @str is successfully parsed, %false otherwise. + * `true` if @str is successfully parsed, `false` otherwise * * Since: 0.9.5 **/ @@ -925,7 +951,7 @@ hb_feature_from_string (const char *str, int len, } if (feature) - memset (feature, 0, sizeof (*feature)); + hb_memset (feature, 0, sizeof (*feature)); return false; } @@ -935,7 +961,7 @@ hb_feature_from_string (const char *str, int len, * @buf: (array length=size) (out): output string * @size: the allocated size of @buf * - * Converts a #hb_feature_t into a %NULL-terminated string in the format + * Converts a #hb_feature_t into a `NULL`-terminated string in the format * understood by hb_feature_from_string(). The client in responsible for * allocating big enough size for @buf, 128 bytes is more than enough. * @@ -955,26 +981,26 @@ hb_feature_to_string (hb_feature_t *feature, len += 4; while (len && s[len - 1] == ' ') len--; - if (feature->start != 0 || feature->end != (unsigned int) -1) + if (feature->start != HB_FEATURE_GLOBAL_START || feature->end != HB_FEATURE_GLOBAL_END) { s[len++] = '['; if (feature->start) - len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start)); + len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start)); if (feature->end != feature->start + 1) { s[len++] = ':'; - if (feature->end != (unsigned int) -1) - len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end)); + if (feature->end != HB_FEATURE_GLOBAL_END) + len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end)); } s[len++] = ']'; } if (feature->value > 1) { s[len++] = '='; - len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value)); + len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value)); } assert (len < ARRAY_LENGTH (s)); - len = MIN (len, size - 1); - memcpy (buf, s, len); + len = hb_min (len, size - 1); + hb_memcpy (buf, s, len); buf[len] = '\0'; } @@ -984,7 +1010,11 @@ static bool parse_variation_value (const char **pp, const char *end, hb_variation_t *variation) { parse_char (pp, end, '='); /* Optional. */ - return parse_float (pp, end, &variation->value); + double v; + if (unlikely (!hb_parse_double (pp, end, &v))) return false; + + variation->value = v; + return true; } static bool @@ -998,6 +1028,21 @@ parse_one_variation (const char **pp, const char *end, hb_variation_t *variation /** * hb_variation_from_string: + * @str: (array length=len) (element-type uint8_t): a string to parse + * @len: length of @str, or -1 if string is `NULL` terminated + * @variation: (out): the #hb_variation_t to initialize with the parsed values + * + * Parses a string into a #hb_variation_t. + * + * The format for specifying variation settings follows. All valid CSS + * font-variation-settings values other than 'normal' and 'inherited' are also + * accepted, though, not documented below. + * + * The format is a tag, optionally followed by an equals sign, followed by a + * number. For example `wght=500`, or `slnt=-7.5`. + * + * Return value: + * `true` if @str is successfully parsed, `false` otherwise * * Since: 1.4.2 */ @@ -1018,12 +1063,60 @@ hb_variation_from_string (const char *str, int len, } if (variation) - memset (variation, 0, sizeof (*variation)); + hb_memset (variation, 0, sizeof (*variation)); return false; } +#ifndef HB_NO_SETLOCALE + +static inline void free_static_C_locale (); + +static struct hb_C_locale_lazy_loader_t : hb_lazy_loader_t, + hb_C_locale_lazy_loader_t> +{ + static hb_locale_t create () + { + hb_locale_t l = newlocale (LC_ALL_MASK, "C", NULL); + if (!l) + return l; + + hb_atexit (free_static_C_locale); + + return l; + } + static void destroy (hb_locale_t l) + { + freelocale (l); + } + static hb_locale_t get_null () + { + return (hb_locale_t) 0; + } +} static_C_locale; + +static inline +void free_static_C_locale () +{ + static_C_locale.free_instance (); +} + +static hb_locale_t +get_C_locale () +{ + return static_C_locale.get_unconst (); +} + +#endif + /** * hb_variation_to_string: + * @variation: an #hb_variation_t to convert + * @buf: (array length=size) (out caller-allocates): output string + * @size: the allocated size of @buf + * + * Converts an #hb_variation_t into a `NULL`-terminated string in the format + * understood by hb_variation_from_string(). The client in responsible for + * allocating big enough size for @buf, 128 bytes is more than enough. * * Since: 1.4.2 */ @@ -1040,10 +1133,88 @@ hb_variation_to_string (hb_variation_t *variation, while (len && s[len - 1] == ' ') len--; s[len++] = '='; - len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", variation->value)); + + hb_locale_t oldlocale HB_UNUSED; + oldlocale = hb_uselocale (get_C_locale ()); + len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", (double) variation->value)); + (void) hb_uselocale (oldlocale); assert (len < ARRAY_LENGTH (s)); - len = MIN (len, size - 1); - memcpy (buf, s, len); + len = hb_min (len, size - 1); + hb_memcpy (buf, s, len); buf[len] = '\0'; } + +/** + * hb_color_get_alpha: + * @color: an #hb_color_t we are interested in its channels. + * + * Fetches the alpha channel of the given @color. + * + * Return value: Alpha channel value + * + * Since: 2.1.0 + */ +uint8_t +(hb_color_get_alpha) (hb_color_t color) +{ + return hb_color_get_alpha (color); +} + +/** + * hb_color_get_red: + * @color: an #hb_color_t we are interested in its channels. + * + * Fetches the red channel of the given @color. + * + * Return value: Red channel value + * + * Since: 2.1.0 + */ +uint8_t +(hb_color_get_red) (hb_color_t color) +{ + return hb_color_get_red (color); +} + +/** + * hb_color_get_green: + * @color: an #hb_color_t we are interested in its channels. + * + * Fetches the green channel of the given @color. + * + * Return value: Green channel value + * + * Since: 2.1.0 + */ +uint8_t +(hb_color_get_green) (hb_color_t color) +{ + return hb_color_get_green (color); +} + +/** + * hb_color_get_blue: + * @color: an #hb_color_t we are interested in its channels. + * + * Fetches the blue channel of the given @color. + * + * Return value: Blue channel value + * + * Since: 2.1.0 + */ +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 + * define anything. Instead, we get it to define one set in here + * only, so only libharfbuzz.so defines them, not other libs. */ +#ifdef HB_NO_VISIBILITY +#undef HB_NO_VISIBILITY +#include "hb-static.cc" +#define HB_NO_VISIBILITY 1 +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-common.h b/src/3rdparty/harfbuzz-ng/src/hb-common.h index 26200ce125b..a9fe666b392 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-common.h +++ b/src/3rdparty/harfbuzz-ng/src/hb-common.h @@ -26,13 +26,17 @@ * Google Author(s): Behdad Esfahbod */ -#ifndef HB_H_IN +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) #error "Include instead." #endif #ifndef HB_COMMON_H #define HB_COMMON_H +#ifndef HB_EXTERN +#define HB_EXTERN extern +#endif + #ifndef HB_BEGIN_DECLS # ifdef __cplusplus # define HB_BEGIN_DECLS extern "C" { @@ -49,17 +53,82 @@ # include #elif defined (_AIX) # include +#elif defined (_MSC_VER) && _MSC_VER < 1600 +/* VS 2010 (_MSC_VER 1600) has stdint.h */ +typedef __int8 int8_t; +typedef unsigned __int8 uint8_t; +typedef __int16 int16_t; +typedef unsigned __int16 uint16_t; +typedef __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#elif defined (__KERNEL__) +# include #else # include #endif +#if defined(__GNUC__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) +#define HB_DEPRECATED __attribute__((__deprecated__)) +#elif defined(_MSC_VER) && (_MSC_VER >= 1300) +#define HB_DEPRECATED __declspec(deprecated) +#else +#define HB_DEPRECATED +#endif + +#if defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) +#define HB_DEPRECATED_FOR(f) __attribute__((__deprecated__("Use '" #f "' instead"))) +#elif defined(_MSC_FULL_VER) && (_MSC_FULL_VER > 140050320) +#define HB_DEPRECATED_FOR(f) __declspec(deprecated("is deprecated. Use '" #f "' instead")) +#else +#define HB_DEPRECATED_FOR(f) HB_DEPRECATED +#endif + + HB_BEGIN_DECLS - +/** + * hb_bool_t: + * + * Data type for booleans. + * + **/ typedef int hb_bool_t; +/** + * hb_codepoint_t: + * + * Data type for holding Unicode codepoints. Also + * used to hold glyph IDs. + * + **/ typedef uint32_t hb_codepoint_t; + +/** + * HB_CODEPOINT_INVALID: + * + * Unused #hb_codepoint_t value. + * + * Since: 8.0.0 + */ +#define HB_CODEPOINT_INVALID ((hb_codepoint_t) -1) + +/** + * hb_position_t: + * + * Data type for holding a single coordinate value. + * Contour points and other multi-dimensional data are + * stored as tuples of #hb_position_t's. + * + **/ typedef int32_t hb_position_t; +/** + * hb_mask_t: + * + * Data type for bitmasks. + * + **/ typedef uint32_t hb_mask_t; typedef union _hb_var_int_t { @@ -71,16 +140,76 @@ typedef union _hb_var_int_t { int8_t i8[4]; } hb_var_int_t; +typedef union _hb_var_num_t { + float f; + uint32_t u32; + int32_t i32; + uint16_t u16[2]; + int16_t i16[2]; + uint8_t u8[4]; + int8_t i8[4]; +} hb_var_num_t; + /* hb_tag_t */ +/** + * hb_tag_t: + * + * Data type for tag identifiers. Tags are four + * byte integers, each byte representing a character. + * + * Tags are used to identify tables, design-variation axes, + * scripts, languages, font features, and baselines with + * human-readable names. + * + **/ typedef uint32_t hb_tag_t; -#define HB_TAG(c1,c2,c3,c4) ((hb_tag_t)((((uint8_t)(c1))<<24)|(((uint8_t)(c2))<<16)|(((uint8_t)(c3))<<8)|((uint8_t)(c4)))) -#define HB_UNTAG(tag) ((uint8_t)((tag)>>24)), ((uint8_t)((tag)>>16)), ((uint8_t)((tag)>>8)), ((uint8_t)(tag)) +/** + * HB_TAG: + * @c1: 1st character of the tag + * @c2: 2nd character of the tag + * @c3: 3rd character of the tag + * @c4: 4th character of the tag + * + * Constructs an #hb_tag_t from four character literals. + * + **/ +#define HB_TAG(c1,c2,c3,c4) ((hb_tag_t)((((uint32_t)(c1)&0xFF)<<24)|(((uint32_t)(c2)&0xFF)<<16)|(((uint32_t)(c3)&0xFF)<<8)|((uint32_t)(c4)&0xFF))) +/** + * HB_UNTAG: + * @tag: an #hb_tag_t + * + * Extracts four character literals from an #hb_tag_t. + * + * Since: 0.6.0 + * + **/ +#define HB_UNTAG(tag) (uint8_t)(((tag)>>24)&0xFF), (uint8_t)(((tag)>>16)&0xFF), (uint8_t)(((tag)>>8)&0xFF), (uint8_t)((tag)&0xFF) + +/** + * HB_TAG_NONE: + * + * Unset #hb_tag_t. + */ #define HB_TAG_NONE HB_TAG(0,0,0,0) +/** + * HB_TAG_MAX: + * + * Maximum possible unsigned #hb_tag_t. + * + * Since: 0.9.26 + */ #define HB_TAG_MAX HB_TAG(0xff,0xff,0xff,0xff) +/** + * HB_TAG_MAX_SIGNED: + * + * Maximum possible signed #hb_tag_t. + * + * Since: 0.9.33 + */ #define HB_TAG_MAX_SIGNED HB_TAG(0x7f,0xff,0xff,0xff) /* len=-1 means str is NUL-terminated. */ @@ -99,6 +228,13 @@ hb_tag_to_string (hb_tag_t tag, char *buf); * @HB_DIRECTION_RTL: Text is set horizontally from right to left. * @HB_DIRECTION_TTB: Text is set vertically from top to bottom. * @HB_DIRECTION_BTT: Text is set vertically from bottom to top. + * + * The direction of a text segment or buffer. + * + * A segment can also be tested for horizontal or vertical + * orientation (irrespective of specific direction) with + * HB_DIRECTION_IS_HORIZONTAL() or HB_DIRECTION_IS_VERTICAL(). + * */ typedef enum { HB_DIRECTION_INVALID = 0, @@ -115,17 +251,71 @@ hb_direction_from_string (const char *str, int len); HB_EXTERN const char * hb_direction_to_string (hb_direction_t direction); +/** + * HB_DIRECTION_IS_VALID: + * @dir: #hb_direction_t to test + * + * Tests whether a text direction is valid. + * + **/ #define HB_DIRECTION_IS_VALID(dir) ((((unsigned int) (dir)) & ~3U) == 4) /* Direction must be valid for the following */ +/** + * HB_DIRECTION_IS_HORIZONTAL: + * @dir: #hb_direction_t to test + * + * Tests whether a text direction is horizontal. Requires + * that the direction be valid. + * + **/ #define HB_DIRECTION_IS_HORIZONTAL(dir) ((((unsigned int) (dir)) & ~1U) == 4) +/** + * HB_DIRECTION_IS_VERTICAL: + * @dir: #hb_direction_t to test + * + * Tests whether a text direction is vertical. Requires + * that the direction be valid. + * + **/ #define HB_DIRECTION_IS_VERTICAL(dir) ((((unsigned int) (dir)) & ~1U) == 6) +/** + * HB_DIRECTION_IS_FORWARD: + * @dir: #hb_direction_t to test + * + * Tests whether a text direction moves forward (from left to right, or from + * top to bottom). Requires that the direction be valid. + * + **/ #define HB_DIRECTION_IS_FORWARD(dir) ((((unsigned int) (dir)) & ~2U) == 4) +/** + * HB_DIRECTION_IS_BACKWARD: + * @dir: #hb_direction_t to test + * + * Tests whether a text direction moves backward (from right to left, or from + * bottom to top). Requires that the direction be valid. + * + **/ #define HB_DIRECTION_IS_BACKWARD(dir) ((((unsigned int) (dir)) & ~2U) == 5) +/** + * HB_DIRECTION_REVERSE: + * @dir: #hb_direction_t to reverse + * + * Reverses a text direction. Requires that the direction + * be valid. + * + **/ #define HB_DIRECTION_REVERSE(dir) ((hb_direction_t) (((unsigned int) (dir)) ^ 1)) /* hb_language_t */ +/** + * hb_language_t: + * + * Data type for languages. Each #hb_language_t corresponds to a BCP 47 + * language tag. + * + */ typedef const struct hb_language_impl_t *hb_language_t; HB_EXTERN hb_language_t @@ -134,198 +324,431 @@ hb_language_from_string (const char *str, int len); HB_EXTERN const char * hb_language_to_string (hb_language_t language); +/** + * HB_LANGUAGE_INVALID: + * + * An unset #hb_language_t. + * + * Since: 0.6.0 + */ #define HB_LANGUAGE_INVALID ((hb_language_t) 0) HB_EXTERN hb_language_t hb_language_get_default (void); +HB_EXTERN hb_bool_t +hb_language_matches (hb_language_t language, + hb_language_t specific); -/* hb_script_t */ +/** + * hb_script_t: + * @HB_SCRIPT_COMMON: `Zyyy` + * @HB_SCRIPT_INHERITED: `Zinh` + * @HB_SCRIPT_UNKNOWN: `Zzzz` + * @HB_SCRIPT_ARABIC: `Arab` + * @HB_SCRIPT_ARMENIAN: `Armn` + * @HB_SCRIPT_BENGALI: `Beng` + * @HB_SCRIPT_CYRILLIC: `Cyrl` + * @HB_SCRIPT_DEVANAGARI: `Deva` + * @HB_SCRIPT_GEORGIAN: `Geor` + * @HB_SCRIPT_GREEK: `Grek` + * @HB_SCRIPT_GUJARATI: `Gujr` + * @HB_SCRIPT_GURMUKHI: `Guru` + * @HB_SCRIPT_HANGUL: `Hang` + * @HB_SCRIPT_HAN: `Hani` + * @HB_SCRIPT_HEBREW: `Hebr` + * @HB_SCRIPT_HIRAGANA: `Hira` + * @HB_SCRIPT_KANNADA: `Knda` + * @HB_SCRIPT_KATAKANA: `Kana` + * @HB_SCRIPT_LAO: `Laoo` + * @HB_SCRIPT_LATIN: `Latn` + * @HB_SCRIPT_MALAYALAM: `Mlym` + * @HB_SCRIPT_ORIYA: `Orya` + * @HB_SCRIPT_TAMIL: `Taml` + * @HB_SCRIPT_TELUGU: `Telu` + * @HB_SCRIPT_THAI: `Thai` + * @HB_SCRIPT_TIBETAN: `Tibt` + * @HB_SCRIPT_BOPOMOFO: `Bopo` + * @HB_SCRIPT_BRAILLE: `Brai` + * @HB_SCRIPT_CANADIAN_SYLLABICS: `Cans` + * @HB_SCRIPT_CHEROKEE: `Cher` + * @HB_SCRIPT_ETHIOPIC: `Ethi` + * @HB_SCRIPT_KHMER: `Khmr` + * @HB_SCRIPT_MONGOLIAN: `Mong` + * @HB_SCRIPT_MYANMAR: `Mymr` + * @HB_SCRIPT_OGHAM: `Ogam` + * @HB_SCRIPT_RUNIC: `Runr` + * @HB_SCRIPT_SINHALA: `Sinh` + * @HB_SCRIPT_SYRIAC: `Syrc` + * @HB_SCRIPT_THAANA: `Thaa` + * @HB_SCRIPT_YI: `Yiii` + * @HB_SCRIPT_DESERET: `Dsrt` + * @HB_SCRIPT_GOTHIC: `Goth` + * @HB_SCRIPT_OLD_ITALIC: `Ital` + * @HB_SCRIPT_BUHID: `Buhd` + * @HB_SCRIPT_HANUNOO: `Hano` + * @HB_SCRIPT_TAGALOG: `Tglg` + * @HB_SCRIPT_TAGBANWA: `Tagb` + * @HB_SCRIPT_CYPRIOT: `Cprt` + * @HB_SCRIPT_LIMBU: `Limb` + * @HB_SCRIPT_LINEAR_B: `Linb` + * @HB_SCRIPT_OSMANYA: `Osma` + * @HB_SCRIPT_SHAVIAN: `Shaw` + * @HB_SCRIPT_TAI_LE: `Tale` + * @HB_SCRIPT_UGARITIC: `Ugar` + * @HB_SCRIPT_BUGINESE: `Bugi` + * @HB_SCRIPT_COPTIC: `Copt` + * @HB_SCRIPT_GLAGOLITIC: `Glag` + * @HB_SCRIPT_KHAROSHTHI: `Khar` + * @HB_SCRIPT_NEW_TAI_LUE: `Talu` + * @HB_SCRIPT_OLD_PERSIAN: `Xpeo` + * @HB_SCRIPT_SYLOTI_NAGRI: `Sylo` + * @HB_SCRIPT_TIFINAGH: `Tfng` + * @HB_SCRIPT_BALINESE: `Bali` + * @HB_SCRIPT_CUNEIFORM: `Xsux` + * @HB_SCRIPT_NKO: `Nkoo` + * @HB_SCRIPT_PHAGS_PA: `Phag` + * @HB_SCRIPT_PHOENICIAN: `Phnx` + * @HB_SCRIPT_CARIAN: `Cari` + * @HB_SCRIPT_CHAM: `Cham` + * @HB_SCRIPT_KAYAH_LI: `Kali` + * @HB_SCRIPT_LEPCHA: `Lepc` + * @HB_SCRIPT_LYCIAN: `Lyci` + * @HB_SCRIPT_LYDIAN: `Lydi` + * @HB_SCRIPT_OL_CHIKI: `Olck` + * @HB_SCRIPT_REJANG: `Rjng` + * @HB_SCRIPT_SAURASHTRA: `Saur` + * @HB_SCRIPT_SUNDANESE: `Sund` + * @HB_SCRIPT_VAI: `Vaii` + * @HB_SCRIPT_AVESTAN: `Avst` + * @HB_SCRIPT_BAMUM: `Bamu` + * @HB_SCRIPT_EGYPTIAN_HIEROGLYPHS: `Egyp` + * @HB_SCRIPT_IMPERIAL_ARAMAIC: `Armi` + * @HB_SCRIPT_INSCRIPTIONAL_PAHLAVI: `Phli` + * @HB_SCRIPT_INSCRIPTIONAL_PARTHIAN: `Prti` + * @HB_SCRIPT_JAVANESE: `Java` + * @HB_SCRIPT_KAITHI: `Kthi` + * @HB_SCRIPT_LISU: `Lisu` + * @HB_SCRIPT_MEETEI_MAYEK: `Mtei` + * @HB_SCRIPT_OLD_SOUTH_ARABIAN: `Sarb` + * @HB_SCRIPT_OLD_TURKIC: `Orkh` + * @HB_SCRIPT_SAMARITAN: `Samr` + * @HB_SCRIPT_TAI_THAM: `Lana` + * @HB_SCRIPT_TAI_VIET: `Tavt` + * @HB_SCRIPT_BATAK: `Batk` + * @HB_SCRIPT_BRAHMI: `Brah` + * @HB_SCRIPT_MANDAIC: `Mand` + * @HB_SCRIPT_CHAKMA: `Cakm` + * @HB_SCRIPT_MEROITIC_CURSIVE: `Merc` + * @HB_SCRIPT_MEROITIC_HIEROGLYPHS: `Mero` + * @HB_SCRIPT_MIAO: `Plrd` + * @HB_SCRIPT_SHARADA: `Shrd` + * @HB_SCRIPT_SORA_SOMPENG: `Sora` + * @HB_SCRIPT_TAKRI: `Takr` + * @HB_SCRIPT_BASSA_VAH: `Bass`, Since: 0.9.30 + * @HB_SCRIPT_CAUCASIAN_ALBANIAN: `Aghb`, Since: 0.9.30 + * @HB_SCRIPT_DUPLOYAN: `Dupl`, Since: 0.9.30 + * @HB_SCRIPT_ELBASAN: `Elba`, Since: 0.9.30 + * @HB_SCRIPT_GRANTHA: `Gran`, Since: 0.9.30 + * @HB_SCRIPT_KHOJKI: `Khoj`, Since: 0.9.30 + * @HB_SCRIPT_KHUDAWADI: `Sind`, Since: 0.9.30 + * @HB_SCRIPT_LINEAR_A: `Lina`, Since: 0.9.30 + * @HB_SCRIPT_MAHAJANI: `Mahj`, Since: 0.9.30 + * @HB_SCRIPT_MANICHAEAN: `Mani`, Since: 0.9.30 + * @HB_SCRIPT_MENDE_KIKAKUI: `Mend`, Since: 0.9.30 + * @HB_SCRIPT_MODI: `Modi`, Since: 0.9.30 + * @HB_SCRIPT_MRO: `Mroo`, Since: 0.9.30 + * @HB_SCRIPT_NABATAEAN: `Nbat`, Since: 0.9.30 + * @HB_SCRIPT_OLD_NORTH_ARABIAN: `Narb`, Since: 0.9.30 + * @HB_SCRIPT_OLD_PERMIC: `Perm`, Since: 0.9.30 + * @HB_SCRIPT_PAHAWH_HMONG: `Hmng`, Since: 0.9.30 + * @HB_SCRIPT_PALMYRENE: `Palm`, Since: 0.9.30 + * @HB_SCRIPT_PAU_CIN_HAU: `Pauc`, Since: 0.9.30 + * @HB_SCRIPT_PSALTER_PAHLAVI: `Phlp`, Since: 0.9.30 + * @HB_SCRIPT_SIDDHAM: `Sidd`, Since: 0.9.30 + * @HB_SCRIPT_TIRHUTA: `Tirh`, Since: 0.9.30 + * @HB_SCRIPT_WARANG_CITI: `Wara`, Since: 0.9.30 + * @HB_SCRIPT_AHOM: `Ahom`, Since: 0.9.30 + * @HB_SCRIPT_ANATOLIAN_HIEROGLYPHS: `Hluw`, Since: 0.9.30 + * @HB_SCRIPT_HATRAN: `Hatr`, Since: 0.9.30 + * @HB_SCRIPT_MULTANI: `Mult`, Since: 0.9.30 + * @HB_SCRIPT_OLD_HUNGARIAN: `Hung`, Since: 0.9.30 + * @HB_SCRIPT_SIGNWRITING: `Sgnw`, Since: 0.9.30 + * @HB_SCRIPT_ADLAM: `Adlm`, Since: 1.3.0 + * @HB_SCRIPT_BHAIKSUKI: `Bhks`, Since: 1.3.0 + * @HB_SCRIPT_MARCHEN: `Marc`, Since: 1.3.0 + * @HB_SCRIPT_OSAGE: `Osge`, Since: 1.3.0 + * @HB_SCRIPT_TANGUT: `Tang`, Since: 1.3.0 + * @HB_SCRIPT_NEWA: `Newa`, Since: 1.3.0 + * @HB_SCRIPT_MASARAM_GONDI: `Gonm`, Since: 1.6.0 + * @HB_SCRIPT_NUSHU: `Nshu`, Since: 1.6.0 + * @HB_SCRIPT_SOYOMBO: `Soyo`, Since: 1.6.0 + * @HB_SCRIPT_ZANABAZAR_SQUARE: `Zanb`, Since: 1.6.0 + * @HB_SCRIPT_DOGRA: `Dogr`, Since: 1.8.0 + * @HB_SCRIPT_GUNJALA_GONDI: `Gong`, Since: 1.8.0 + * @HB_SCRIPT_HANIFI_ROHINGYA: `Rohg`, Since: 1.8.0 + * @HB_SCRIPT_MAKASAR: `Maka`, Since: 1.8.0 + * @HB_SCRIPT_MEDEFAIDRIN: `Medf`, Since: 1.8.0 + * @HB_SCRIPT_OLD_SOGDIAN: `Sogo`, Since: 1.8.0 + * @HB_SCRIPT_SOGDIAN: `Sogd`, Since: 1.8.0 + * @HB_SCRIPT_ELYMAIC: `Elym`, Since: 2.4.0 + * @HB_SCRIPT_NANDINAGARI: `Nand`, Since: 2.4.0 + * @HB_SCRIPT_NYIAKENG_PUACHUE_HMONG: `Hmnp`, Since: 2.4.0 + * @HB_SCRIPT_WANCHO: `Wcho`, Since: 2.4.0 + * @HB_SCRIPT_CHORASMIAN: `Chrs`, Since: 2.6.7 + * @HB_SCRIPT_DIVES_AKURU: `Diak`, Since: 2.6.7 + * @HB_SCRIPT_KHITAN_SMALL_SCRIPT: `Kits`, Since: 2.6.7 + * @HB_SCRIPT_YEZIDI: `Yezi`, Since: 2.6.7 + * @HB_SCRIPT_CYPRO_MINOAN: `Cpmn`, Since: 3.0.0 + * @HB_SCRIPT_OLD_UYGHUR: `Ougr`, Since: 3.0.0 + * @HB_SCRIPT_TANGSA: `Tnsa`, Since: 3.0.0 + * @HB_SCRIPT_TOTO: `Toto`, Since: 3.0.0 + * @HB_SCRIPT_VITHKUQI: `Vith`, Since: 3.0.0 + * @HB_SCRIPT_MATH: `Zmth`, Since: 3.4.0 + * @HB_SCRIPT_KAWI: `Kawi`, Since: 5.2.0 + * @HB_SCRIPT_NAG_MUNDARI: `Nagm`, Since: 5.2.0 + * @HB_SCRIPT_INVALID: No script set + * + * Data type for scripts. Each #hb_script_t's value is an #hb_tag_t corresponding + * to the four-letter values defined by [ISO 15924](https://unicode.org/iso15924/). + * + * See also the Script (sc) property of the Unicode Character Database. + * + **/ -/* http://unicode.org/iso15924/ */ -/* http://goo.gl/x9ilM */ -/* Unicode Character Database property: Script (sc) */ +/* https://docs.google.com/spreadsheets/d/1Y90M0Ie3MUJ6UVCRDOypOtijlMDLNNyyLk36T6iMu0o */ typedef enum { - /*1.1*/ HB_SCRIPT_COMMON = HB_TAG ('Z','y','y','y'), - /*1.1*/ HB_SCRIPT_INHERITED = HB_TAG ('Z','i','n','h'), - /*5.0*/ HB_SCRIPT_UNKNOWN = HB_TAG ('Z','z','z','z'), + HB_SCRIPT_COMMON = HB_TAG ('Z','y','y','y'), /*1.1*/ + HB_SCRIPT_INHERITED = HB_TAG ('Z','i','n','h'), /*1.1*/ + HB_SCRIPT_UNKNOWN = HB_TAG ('Z','z','z','z'), /*5.0*/ - /*1.1*/ HB_SCRIPT_ARABIC = HB_TAG ('A','r','a','b'), - /*1.1*/ HB_SCRIPT_ARMENIAN = HB_TAG ('A','r','m','n'), - /*1.1*/ HB_SCRIPT_BENGALI = HB_TAG ('B','e','n','g'), - /*1.1*/ HB_SCRIPT_CYRILLIC = HB_TAG ('C','y','r','l'), - /*1.1*/ HB_SCRIPT_DEVANAGARI = HB_TAG ('D','e','v','a'), - /*1.1*/ HB_SCRIPT_GEORGIAN = HB_TAG ('G','e','o','r'), - /*1.1*/ HB_SCRIPT_GREEK = HB_TAG ('G','r','e','k'), - /*1.1*/ HB_SCRIPT_GUJARATI = HB_TAG ('G','u','j','r'), - /*1.1*/ HB_SCRIPT_GURMUKHI = HB_TAG ('G','u','r','u'), - /*1.1*/ HB_SCRIPT_HANGUL = HB_TAG ('H','a','n','g'), - /*1.1*/ HB_SCRIPT_HAN = HB_TAG ('H','a','n','i'), - /*1.1*/ HB_SCRIPT_HEBREW = HB_TAG ('H','e','b','r'), - /*1.1*/ HB_SCRIPT_HIRAGANA = HB_TAG ('H','i','r','a'), - /*1.1*/ HB_SCRIPT_KANNADA = HB_TAG ('K','n','d','a'), - /*1.1*/ HB_SCRIPT_KATAKANA = HB_TAG ('K','a','n','a'), - /*1.1*/ HB_SCRIPT_LAO = HB_TAG ('L','a','o','o'), - /*1.1*/ HB_SCRIPT_LATIN = HB_TAG ('L','a','t','n'), - /*1.1*/ HB_SCRIPT_MALAYALAM = HB_TAG ('M','l','y','m'), - /*1.1*/ HB_SCRIPT_ORIYA = HB_TAG ('O','r','y','a'), - /*1.1*/ HB_SCRIPT_TAMIL = HB_TAG ('T','a','m','l'), - /*1.1*/ HB_SCRIPT_TELUGU = HB_TAG ('T','e','l','u'), - /*1.1*/ HB_SCRIPT_THAI = HB_TAG ('T','h','a','i'), + HB_SCRIPT_ARABIC = HB_TAG ('A','r','a','b'), /*1.1*/ + HB_SCRIPT_ARMENIAN = HB_TAG ('A','r','m','n'), /*1.1*/ + HB_SCRIPT_BENGALI = HB_TAG ('B','e','n','g'), /*1.1*/ + HB_SCRIPT_CYRILLIC = HB_TAG ('C','y','r','l'), /*1.1*/ + HB_SCRIPT_DEVANAGARI = HB_TAG ('D','e','v','a'), /*1.1*/ + HB_SCRIPT_GEORGIAN = HB_TAG ('G','e','o','r'), /*1.1*/ + HB_SCRIPT_GREEK = HB_TAG ('G','r','e','k'), /*1.1*/ + HB_SCRIPT_GUJARATI = HB_TAG ('G','u','j','r'), /*1.1*/ + HB_SCRIPT_GURMUKHI = HB_TAG ('G','u','r','u'), /*1.1*/ + HB_SCRIPT_HANGUL = HB_TAG ('H','a','n','g'), /*1.1*/ + HB_SCRIPT_HAN = HB_TAG ('H','a','n','i'), /*1.1*/ + HB_SCRIPT_HEBREW = HB_TAG ('H','e','b','r'), /*1.1*/ + HB_SCRIPT_HIRAGANA = HB_TAG ('H','i','r','a'), /*1.1*/ + HB_SCRIPT_KANNADA = HB_TAG ('K','n','d','a'), /*1.1*/ + HB_SCRIPT_KATAKANA = HB_TAG ('K','a','n','a'), /*1.1*/ + HB_SCRIPT_LAO = HB_TAG ('L','a','o','o'), /*1.1*/ + HB_SCRIPT_LATIN = HB_TAG ('L','a','t','n'), /*1.1*/ + HB_SCRIPT_MALAYALAM = HB_TAG ('M','l','y','m'), /*1.1*/ + HB_SCRIPT_ORIYA = HB_TAG ('O','r','y','a'), /*1.1*/ + HB_SCRIPT_TAMIL = HB_TAG ('T','a','m','l'), /*1.1*/ + HB_SCRIPT_TELUGU = HB_TAG ('T','e','l','u'), /*1.1*/ + HB_SCRIPT_THAI = HB_TAG ('T','h','a','i'), /*1.1*/ - /*2.0*/ HB_SCRIPT_TIBETAN = HB_TAG ('T','i','b','t'), + HB_SCRIPT_TIBETAN = HB_TAG ('T','i','b','t'), /*2.0*/ - /*3.0*/ HB_SCRIPT_BOPOMOFO = HB_TAG ('B','o','p','o'), - /*3.0*/ HB_SCRIPT_BRAILLE = HB_TAG ('B','r','a','i'), - /*3.0*/ HB_SCRIPT_CANADIAN_SYLLABICS = HB_TAG ('C','a','n','s'), - /*3.0*/ HB_SCRIPT_CHEROKEE = HB_TAG ('C','h','e','r'), - /*3.0*/ HB_SCRIPT_ETHIOPIC = HB_TAG ('E','t','h','i'), - /*3.0*/ HB_SCRIPT_KHMER = HB_TAG ('K','h','m','r'), - /*3.0*/ HB_SCRIPT_MONGOLIAN = HB_TAG ('M','o','n','g'), - /*3.0*/ HB_SCRIPT_MYANMAR = HB_TAG ('M','y','m','r'), - /*3.0*/ HB_SCRIPT_OGHAM = HB_TAG ('O','g','a','m'), - /*3.0*/ HB_SCRIPT_RUNIC = HB_TAG ('R','u','n','r'), - /*3.0*/ HB_SCRIPT_SINHALA = HB_TAG ('S','i','n','h'), - /*3.0*/ HB_SCRIPT_SYRIAC = HB_TAG ('S','y','r','c'), - /*3.0*/ HB_SCRIPT_THAANA = HB_TAG ('T','h','a','a'), - /*3.0*/ HB_SCRIPT_YI = HB_TAG ('Y','i','i','i'), + HB_SCRIPT_BOPOMOFO = HB_TAG ('B','o','p','o'), /*3.0*/ + HB_SCRIPT_BRAILLE = HB_TAG ('B','r','a','i'), /*3.0*/ + HB_SCRIPT_CANADIAN_SYLLABICS = HB_TAG ('C','a','n','s'), /*3.0*/ + HB_SCRIPT_CHEROKEE = HB_TAG ('C','h','e','r'), /*3.0*/ + HB_SCRIPT_ETHIOPIC = HB_TAG ('E','t','h','i'), /*3.0*/ + HB_SCRIPT_KHMER = HB_TAG ('K','h','m','r'), /*3.0*/ + HB_SCRIPT_MONGOLIAN = HB_TAG ('M','o','n','g'), /*3.0*/ + HB_SCRIPT_MYANMAR = HB_TAG ('M','y','m','r'), /*3.0*/ + HB_SCRIPT_OGHAM = HB_TAG ('O','g','a','m'), /*3.0*/ + HB_SCRIPT_RUNIC = HB_TAG ('R','u','n','r'), /*3.0*/ + HB_SCRIPT_SINHALA = HB_TAG ('S','i','n','h'), /*3.0*/ + HB_SCRIPT_SYRIAC = HB_TAG ('S','y','r','c'), /*3.0*/ + HB_SCRIPT_THAANA = HB_TAG ('T','h','a','a'), /*3.0*/ + HB_SCRIPT_YI = HB_TAG ('Y','i','i','i'), /*3.0*/ - /*3.1*/ HB_SCRIPT_DESERET = HB_TAG ('D','s','r','t'), - /*3.1*/ HB_SCRIPT_GOTHIC = HB_TAG ('G','o','t','h'), - /*3.1*/ HB_SCRIPT_OLD_ITALIC = HB_TAG ('I','t','a','l'), + HB_SCRIPT_DESERET = HB_TAG ('D','s','r','t'), /*3.1*/ + HB_SCRIPT_GOTHIC = HB_TAG ('G','o','t','h'), /*3.1*/ + HB_SCRIPT_OLD_ITALIC = HB_TAG ('I','t','a','l'), /*3.1*/ - /*3.2*/ HB_SCRIPT_BUHID = HB_TAG ('B','u','h','d'), - /*3.2*/ HB_SCRIPT_HANUNOO = HB_TAG ('H','a','n','o'), - /*3.2*/ HB_SCRIPT_TAGALOG = HB_TAG ('T','g','l','g'), - /*3.2*/ HB_SCRIPT_TAGBANWA = HB_TAG ('T','a','g','b'), + HB_SCRIPT_BUHID = HB_TAG ('B','u','h','d'), /*3.2*/ + HB_SCRIPT_HANUNOO = HB_TAG ('H','a','n','o'), /*3.2*/ + HB_SCRIPT_TAGALOG = HB_TAG ('T','g','l','g'), /*3.2*/ + HB_SCRIPT_TAGBANWA = HB_TAG ('T','a','g','b'), /*3.2*/ - /*4.0*/ HB_SCRIPT_CYPRIOT = HB_TAG ('C','p','r','t'), - /*4.0*/ HB_SCRIPT_LIMBU = HB_TAG ('L','i','m','b'), - /*4.0*/ HB_SCRIPT_LINEAR_B = HB_TAG ('L','i','n','b'), - /*4.0*/ HB_SCRIPT_OSMANYA = HB_TAG ('O','s','m','a'), - /*4.0*/ HB_SCRIPT_SHAVIAN = HB_TAG ('S','h','a','w'), - /*4.0*/ HB_SCRIPT_TAI_LE = HB_TAG ('T','a','l','e'), - /*4.0*/ HB_SCRIPT_UGARITIC = HB_TAG ('U','g','a','r'), + HB_SCRIPT_CYPRIOT = HB_TAG ('C','p','r','t'), /*4.0*/ + HB_SCRIPT_LIMBU = HB_TAG ('L','i','m','b'), /*4.0*/ + HB_SCRIPT_LINEAR_B = HB_TAG ('L','i','n','b'), /*4.0*/ + HB_SCRIPT_OSMANYA = HB_TAG ('O','s','m','a'), /*4.0*/ + HB_SCRIPT_SHAVIAN = HB_TAG ('S','h','a','w'), /*4.0*/ + HB_SCRIPT_TAI_LE = HB_TAG ('T','a','l','e'), /*4.0*/ + HB_SCRIPT_UGARITIC = HB_TAG ('U','g','a','r'), /*4.0*/ - /*4.1*/ HB_SCRIPT_BUGINESE = HB_TAG ('B','u','g','i'), - /*4.1*/ HB_SCRIPT_COPTIC = HB_TAG ('C','o','p','t'), - /*4.1*/ HB_SCRIPT_GLAGOLITIC = HB_TAG ('G','l','a','g'), - /*4.1*/ HB_SCRIPT_KHAROSHTHI = HB_TAG ('K','h','a','r'), - /*4.1*/ HB_SCRIPT_NEW_TAI_LUE = HB_TAG ('T','a','l','u'), - /*4.1*/ HB_SCRIPT_OLD_PERSIAN = HB_TAG ('X','p','e','o'), - /*4.1*/ HB_SCRIPT_SYLOTI_NAGRI = HB_TAG ('S','y','l','o'), - /*4.1*/ HB_SCRIPT_TIFINAGH = HB_TAG ('T','f','n','g'), + HB_SCRIPT_BUGINESE = HB_TAG ('B','u','g','i'), /*4.1*/ + HB_SCRIPT_COPTIC = HB_TAG ('C','o','p','t'), /*4.1*/ + HB_SCRIPT_GLAGOLITIC = HB_TAG ('G','l','a','g'), /*4.1*/ + HB_SCRIPT_KHAROSHTHI = HB_TAG ('K','h','a','r'), /*4.1*/ + HB_SCRIPT_NEW_TAI_LUE = HB_TAG ('T','a','l','u'), /*4.1*/ + HB_SCRIPT_OLD_PERSIAN = HB_TAG ('X','p','e','o'), /*4.1*/ + HB_SCRIPT_SYLOTI_NAGRI = HB_TAG ('S','y','l','o'), /*4.1*/ + HB_SCRIPT_TIFINAGH = HB_TAG ('T','f','n','g'), /*4.1*/ - /*5.0*/ HB_SCRIPT_BALINESE = HB_TAG ('B','a','l','i'), - /*5.0*/ HB_SCRIPT_CUNEIFORM = HB_TAG ('X','s','u','x'), - /*5.0*/ HB_SCRIPT_NKO = HB_TAG ('N','k','o','o'), - /*5.0*/ HB_SCRIPT_PHAGS_PA = HB_TAG ('P','h','a','g'), - /*5.0*/ HB_SCRIPT_PHOENICIAN = HB_TAG ('P','h','n','x'), + HB_SCRIPT_BALINESE = HB_TAG ('B','a','l','i'), /*5.0*/ + HB_SCRIPT_CUNEIFORM = HB_TAG ('X','s','u','x'), /*5.0*/ + HB_SCRIPT_NKO = HB_TAG ('N','k','o','o'), /*5.0*/ + HB_SCRIPT_PHAGS_PA = HB_TAG ('P','h','a','g'), /*5.0*/ + HB_SCRIPT_PHOENICIAN = HB_TAG ('P','h','n','x'), /*5.0*/ - /*5.1*/ HB_SCRIPT_CARIAN = HB_TAG ('C','a','r','i'), - /*5.1*/ HB_SCRIPT_CHAM = HB_TAG ('C','h','a','m'), - /*5.1*/ HB_SCRIPT_KAYAH_LI = HB_TAG ('K','a','l','i'), - /*5.1*/ HB_SCRIPT_LEPCHA = HB_TAG ('L','e','p','c'), - /*5.1*/ HB_SCRIPT_LYCIAN = HB_TAG ('L','y','c','i'), - /*5.1*/ HB_SCRIPT_LYDIAN = HB_TAG ('L','y','d','i'), - /*5.1*/ HB_SCRIPT_OL_CHIKI = HB_TAG ('O','l','c','k'), - /*5.1*/ HB_SCRIPT_REJANG = HB_TAG ('R','j','n','g'), - /*5.1*/ HB_SCRIPT_SAURASHTRA = HB_TAG ('S','a','u','r'), - /*5.1*/ HB_SCRIPT_SUNDANESE = HB_TAG ('S','u','n','d'), - /*5.1*/ HB_SCRIPT_VAI = HB_TAG ('V','a','i','i'), + HB_SCRIPT_CARIAN = HB_TAG ('C','a','r','i'), /*5.1*/ + HB_SCRIPT_CHAM = HB_TAG ('C','h','a','m'), /*5.1*/ + HB_SCRIPT_KAYAH_LI = HB_TAG ('K','a','l','i'), /*5.1*/ + HB_SCRIPT_LEPCHA = HB_TAG ('L','e','p','c'), /*5.1*/ + HB_SCRIPT_LYCIAN = HB_TAG ('L','y','c','i'), /*5.1*/ + HB_SCRIPT_LYDIAN = HB_TAG ('L','y','d','i'), /*5.1*/ + HB_SCRIPT_OL_CHIKI = HB_TAG ('O','l','c','k'), /*5.1*/ + HB_SCRIPT_REJANG = HB_TAG ('R','j','n','g'), /*5.1*/ + HB_SCRIPT_SAURASHTRA = HB_TAG ('S','a','u','r'), /*5.1*/ + HB_SCRIPT_SUNDANESE = HB_TAG ('S','u','n','d'), /*5.1*/ + HB_SCRIPT_VAI = HB_TAG ('V','a','i','i'), /*5.1*/ - /*5.2*/ HB_SCRIPT_AVESTAN = HB_TAG ('A','v','s','t'), - /*5.2*/ HB_SCRIPT_BAMUM = HB_TAG ('B','a','m','u'), - /*5.2*/ HB_SCRIPT_EGYPTIAN_HIEROGLYPHS = HB_TAG ('E','g','y','p'), - /*5.2*/ HB_SCRIPT_IMPERIAL_ARAMAIC = HB_TAG ('A','r','m','i'), - /*5.2*/ HB_SCRIPT_INSCRIPTIONAL_PAHLAVI = HB_TAG ('P','h','l','i'), - /*5.2*/ HB_SCRIPT_INSCRIPTIONAL_PARTHIAN = HB_TAG ('P','r','t','i'), - /*5.2*/ HB_SCRIPT_JAVANESE = HB_TAG ('J','a','v','a'), - /*5.2*/ HB_SCRIPT_KAITHI = HB_TAG ('K','t','h','i'), - /*5.2*/ HB_SCRIPT_LISU = HB_TAG ('L','i','s','u'), - /*5.2*/ HB_SCRIPT_MEETEI_MAYEK = HB_TAG ('M','t','e','i'), - /*5.2*/ HB_SCRIPT_OLD_SOUTH_ARABIAN = HB_TAG ('S','a','r','b'), - /*5.2*/ HB_SCRIPT_OLD_TURKIC = HB_TAG ('O','r','k','h'), - /*5.2*/ HB_SCRIPT_SAMARITAN = HB_TAG ('S','a','m','r'), - /*5.2*/ HB_SCRIPT_TAI_THAM = HB_TAG ('L','a','n','a'), - /*5.2*/ HB_SCRIPT_TAI_VIET = HB_TAG ('T','a','v','t'), + HB_SCRIPT_AVESTAN = HB_TAG ('A','v','s','t'), /*5.2*/ + HB_SCRIPT_BAMUM = HB_TAG ('B','a','m','u'), /*5.2*/ + HB_SCRIPT_EGYPTIAN_HIEROGLYPHS = HB_TAG ('E','g','y','p'), /*5.2*/ + HB_SCRIPT_IMPERIAL_ARAMAIC = HB_TAG ('A','r','m','i'), /*5.2*/ + HB_SCRIPT_INSCRIPTIONAL_PAHLAVI = HB_TAG ('P','h','l','i'), /*5.2*/ + HB_SCRIPT_INSCRIPTIONAL_PARTHIAN = HB_TAG ('P','r','t','i'), /*5.2*/ + HB_SCRIPT_JAVANESE = HB_TAG ('J','a','v','a'), /*5.2*/ + HB_SCRIPT_KAITHI = HB_TAG ('K','t','h','i'), /*5.2*/ + HB_SCRIPT_LISU = HB_TAG ('L','i','s','u'), /*5.2*/ + HB_SCRIPT_MEETEI_MAYEK = HB_TAG ('M','t','e','i'), /*5.2*/ + HB_SCRIPT_OLD_SOUTH_ARABIAN = HB_TAG ('S','a','r','b'), /*5.2*/ + HB_SCRIPT_OLD_TURKIC = HB_TAG ('O','r','k','h'), /*5.2*/ + HB_SCRIPT_SAMARITAN = HB_TAG ('S','a','m','r'), /*5.2*/ + HB_SCRIPT_TAI_THAM = HB_TAG ('L','a','n','a'), /*5.2*/ + HB_SCRIPT_TAI_VIET = HB_TAG ('T','a','v','t'), /*5.2*/ - /*6.0*/ HB_SCRIPT_BATAK = HB_TAG ('B','a','t','k'), - /*6.0*/ HB_SCRIPT_BRAHMI = HB_TAG ('B','r','a','h'), - /*6.0*/ HB_SCRIPT_MANDAIC = HB_TAG ('M','a','n','d'), + HB_SCRIPT_BATAK = HB_TAG ('B','a','t','k'), /*6.0*/ + HB_SCRIPT_BRAHMI = HB_TAG ('B','r','a','h'), /*6.0*/ + HB_SCRIPT_MANDAIC = HB_TAG ('M','a','n','d'), /*6.0*/ - /*6.1*/ HB_SCRIPT_CHAKMA = HB_TAG ('C','a','k','m'), - /*6.1*/ HB_SCRIPT_MEROITIC_CURSIVE = HB_TAG ('M','e','r','c'), - /*6.1*/ HB_SCRIPT_MEROITIC_HIEROGLYPHS = HB_TAG ('M','e','r','o'), - /*6.1*/ HB_SCRIPT_MIAO = HB_TAG ('P','l','r','d'), - /*6.1*/ HB_SCRIPT_SHARADA = HB_TAG ('S','h','r','d'), - /*6.1*/ HB_SCRIPT_SORA_SOMPENG = HB_TAG ('S','o','r','a'), - /*6.1*/ HB_SCRIPT_TAKRI = HB_TAG ('T','a','k','r'), + HB_SCRIPT_CHAKMA = HB_TAG ('C','a','k','m'), /*6.1*/ + HB_SCRIPT_MEROITIC_CURSIVE = HB_TAG ('M','e','r','c'), /*6.1*/ + HB_SCRIPT_MEROITIC_HIEROGLYPHS = HB_TAG ('M','e','r','o'), /*6.1*/ + HB_SCRIPT_MIAO = HB_TAG ('P','l','r','d'), /*6.1*/ + HB_SCRIPT_SHARADA = HB_TAG ('S','h','r','d'), /*6.1*/ + HB_SCRIPT_SORA_SOMPENG = HB_TAG ('S','o','r','a'), /*6.1*/ + HB_SCRIPT_TAKRI = HB_TAG ('T','a','k','r'), /*6.1*/ /* * Since: 0.9.30 */ - /*7.0*/ HB_SCRIPT_BASSA_VAH = HB_TAG ('B','a','s','s'), - /*7.0*/ HB_SCRIPT_CAUCASIAN_ALBANIAN = HB_TAG ('A','g','h','b'), - /*7.0*/ HB_SCRIPT_DUPLOYAN = HB_TAG ('D','u','p','l'), - /*7.0*/ HB_SCRIPT_ELBASAN = HB_TAG ('E','l','b','a'), - /*7.0*/ HB_SCRIPT_GRANTHA = HB_TAG ('G','r','a','n'), - /*7.0*/ HB_SCRIPT_KHOJKI = HB_TAG ('K','h','o','j'), - /*7.0*/ HB_SCRIPT_KHUDAWADI = HB_TAG ('S','i','n','d'), - /*7.0*/ HB_SCRIPT_LINEAR_A = HB_TAG ('L','i','n','a'), - /*7.0*/ HB_SCRIPT_MAHAJANI = HB_TAG ('M','a','h','j'), - /*7.0*/ HB_SCRIPT_MANICHAEAN = HB_TAG ('M','a','n','i'), - /*7.0*/ HB_SCRIPT_MENDE_KIKAKUI = HB_TAG ('M','e','n','d'), - /*7.0*/ HB_SCRIPT_MODI = HB_TAG ('M','o','d','i'), - /*7.0*/ HB_SCRIPT_MRO = HB_TAG ('M','r','o','o'), - /*7.0*/ HB_SCRIPT_NABATAEAN = HB_TAG ('N','b','a','t'), - /*7.0*/ HB_SCRIPT_OLD_NORTH_ARABIAN = HB_TAG ('N','a','r','b'), - /*7.0*/ HB_SCRIPT_OLD_PERMIC = HB_TAG ('P','e','r','m'), - /*7.0*/ HB_SCRIPT_PAHAWH_HMONG = HB_TAG ('H','m','n','g'), - /*7.0*/ HB_SCRIPT_PALMYRENE = HB_TAG ('P','a','l','m'), - /*7.0*/ HB_SCRIPT_PAU_CIN_HAU = HB_TAG ('P','a','u','c'), - /*7.0*/ HB_SCRIPT_PSALTER_PAHLAVI = HB_TAG ('P','h','l','p'), - /*7.0*/ HB_SCRIPT_SIDDHAM = HB_TAG ('S','i','d','d'), - /*7.0*/ HB_SCRIPT_TIRHUTA = HB_TAG ('T','i','r','h'), - /*7.0*/ HB_SCRIPT_WARANG_CITI = HB_TAG ('W','a','r','a'), + HB_SCRIPT_BASSA_VAH = HB_TAG ('B','a','s','s'), /*7.0*/ + HB_SCRIPT_CAUCASIAN_ALBANIAN = HB_TAG ('A','g','h','b'), /*7.0*/ + HB_SCRIPT_DUPLOYAN = HB_TAG ('D','u','p','l'), /*7.0*/ + HB_SCRIPT_ELBASAN = HB_TAG ('E','l','b','a'), /*7.0*/ + HB_SCRIPT_GRANTHA = HB_TAG ('G','r','a','n'), /*7.0*/ + HB_SCRIPT_KHOJKI = HB_TAG ('K','h','o','j'), /*7.0*/ + HB_SCRIPT_KHUDAWADI = HB_TAG ('S','i','n','d'), /*7.0*/ + HB_SCRIPT_LINEAR_A = HB_TAG ('L','i','n','a'), /*7.0*/ + HB_SCRIPT_MAHAJANI = HB_TAG ('M','a','h','j'), /*7.0*/ + HB_SCRIPT_MANICHAEAN = HB_TAG ('M','a','n','i'), /*7.0*/ + HB_SCRIPT_MENDE_KIKAKUI = HB_TAG ('M','e','n','d'), /*7.0*/ + HB_SCRIPT_MODI = HB_TAG ('M','o','d','i'), /*7.0*/ + HB_SCRIPT_MRO = HB_TAG ('M','r','o','o'), /*7.0*/ + HB_SCRIPT_NABATAEAN = HB_TAG ('N','b','a','t'), /*7.0*/ + HB_SCRIPT_OLD_NORTH_ARABIAN = HB_TAG ('N','a','r','b'), /*7.0*/ + HB_SCRIPT_OLD_PERMIC = HB_TAG ('P','e','r','m'), /*7.0*/ + HB_SCRIPT_PAHAWH_HMONG = HB_TAG ('H','m','n','g'), /*7.0*/ + HB_SCRIPT_PALMYRENE = HB_TAG ('P','a','l','m'), /*7.0*/ + HB_SCRIPT_PAU_CIN_HAU = HB_TAG ('P','a','u','c'), /*7.0*/ + HB_SCRIPT_PSALTER_PAHLAVI = HB_TAG ('P','h','l','p'), /*7.0*/ + HB_SCRIPT_SIDDHAM = HB_TAG ('S','i','d','d'), /*7.0*/ + HB_SCRIPT_TIRHUTA = HB_TAG ('T','i','r','h'), /*7.0*/ + HB_SCRIPT_WARANG_CITI = HB_TAG ('W','a','r','a'), /*7.0*/ - /*8.0*/ HB_SCRIPT_AHOM = HB_TAG ('A','h','o','m'), - /*8.0*/ HB_SCRIPT_ANATOLIAN_HIEROGLYPHS = HB_TAG ('H','l','u','w'), - /*8.0*/ HB_SCRIPT_HATRAN = HB_TAG ('H','a','t','r'), - /*8.0*/ HB_SCRIPT_MULTANI = HB_TAG ('M','u','l','t'), - /*8.0*/ HB_SCRIPT_OLD_HUNGARIAN = HB_TAG ('H','u','n','g'), - /*8.0*/ HB_SCRIPT_SIGNWRITING = HB_TAG ('S','g','n','w'), + HB_SCRIPT_AHOM = HB_TAG ('A','h','o','m'), /*8.0*/ + HB_SCRIPT_ANATOLIAN_HIEROGLYPHS = HB_TAG ('H','l','u','w'), /*8.0*/ + HB_SCRIPT_HATRAN = HB_TAG ('H','a','t','r'), /*8.0*/ + HB_SCRIPT_MULTANI = HB_TAG ('M','u','l','t'), /*8.0*/ + HB_SCRIPT_OLD_HUNGARIAN = HB_TAG ('H','u','n','g'), /*8.0*/ + HB_SCRIPT_SIGNWRITING = HB_TAG ('S','g','n','w'), /*8.0*/ /* * Since 1.3.0 */ - /*9.0*/ HB_SCRIPT_ADLAM = HB_TAG ('A','d','l','m'), - /*9.0*/ HB_SCRIPT_BHAIKSUKI = HB_TAG ('B','h','k','s'), - /*9.0*/ HB_SCRIPT_MARCHEN = HB_TAG ('M','a','r','c'), - /*9.0*/ HB_SCRIPT_OSAGE = HB_TAG ('O','s','g','e'), - /*9.0*/ HB_SCRIPT_TANGUT = HB_TAG ('T','a','n','g'), - /*9.0*/ HB_SCRIPT_NEWA = HB_TAG ('N','e','w','a'), + HB_SCRIPT_ADLAM = HB_TAG ('A','d','l','m'), /*9.0*/ + HB_SCRIPT_BHAIKSUKI = HB_TAG ('B','h','k','s'), /*9.0*/ + HB_SCRIPT_MARCHEN = HB_TAG ('M','a','r','c'), /*9.0*/ + HB_SCRIPT_OSAGE = HB_TAG ('O','s','g','e'), /*9.0*/ + HB_SCRIPT_TANGUT = HB_TAG ('T','a','n','g'), /*9.0*/ + HB_SCRIPT_NEWA = HB_TAG ('N','e','w','a'), /*9.0*/ /* * Since 1.6.0 */ - /*10.0*/HB_SCRIPT_MASARAM_GONDI = HB_TAG ('G','o','n','m'), - /*10.0*/HB_SCRIPT_NUSHU = HB_TAG ('N','s','h','u'), - /*10.0*/HB_SCRIPT_SOYOMBO = HB_TAG ('S','o','y','o'), - /*10.0*/HB_SCRIPT_ZANABAZAR_SQUARE = HB_TAG ('Z','a','n','b'), + HB_SCRIPT_MASARAM_GONDI = HB_TAG ('G','o','n','m'), /*10.0*/ + HB_SCRIPT_NUSHU = HB_TAG ('N','s','h','u'), /*10.0*/ + HB_SCRIPT_SOYOMBO = HB_TAG ('S','o','y','o'), /*10.0*/ + HB_SCRIPT_ZANABAZAR_SQUARE = HB_TAG ('Z','a','n','b'), /*10.0*/ + + /* + * Since 1.8.0 + */ + HB_SCRIPT_DOGRA = HB_TAG ('D','o','g','r'), /*11.0*/ + HB_SCRIPT_GUNJALA_GONDI = HB_TAG ('G','o','n','g'), /*11.0*/ + HB_SCRIPT_HANIFI_ROHINGYA = HB_TAG ('R','o','h','g'), /*11.0*/ + HB_SCRIPT_MAKASAR = HB_TAG ('M','a','k','a'), /*11.0*/ + HB_SCRIPT_MEDEFAIDRIN = HB_TAG ('M','e','d','f'), /*11.0*/ + HB_SCRIPT_OLD_SOGDIAN = HB_TAG ('S','o','g','o'), /*11.0*/ + HB_SCRIPT_SOGDIAN = HB_TAG ('S','o','g','d'), /*11.0*/ + + /* + * Since 2.4.0 + */ + HB_SCRIPT_ELYMAIC = HB_TAG ('E','l','y','m'), /*12.0*/ + HB_SCRIPT_NANDINAGARI = HB_TAG ('N','a','n','d'), /*12.0*/ + HB_SCRIPT_NYIAKENG_PUACHUE_HMONG = HB_TAG ('H','m','n','p'), /*12.0*/ + HB_SCRIPT_WANCHO = HB_TAG ('W','c','h','o'), /*12.0*/ + + /* + * Since 2.6.7 + */ + HB_SCRIPT_CHORASMIAN = HB_TAG ('C','h','r','s'), /*13.0*/ + HB_SCRIPT_DIVES_AKURU = HB_TAG ('D','i','a','k'), /*13.0*/ + HB_SCRIPT_KHITAN_SMALL_SCRIPT = HB_TAG ('K','i','t','s'), /*13.0*/ + HB_SCRIPT_YEZIDI = HB_TAG ('Y','e','z','i'), /*13.0*/ + + /* + * Since 3.0.0 + */ + HB_SCRIPT_CYPRO_MINOAN = HB_TAG ('C','p','m','n'), /*14.0*/ + HB_SCRIPT_OLD_UYGHUR = HB_TAG ('O','u','g','r'), /*14.0*/ + HB_SCRIPT_TANGSA = HB_TAG ('T','n','s','a'), /*14.0*/ + HB_SCRIPT_TOTO = HB_TAG ('T','o','t','o'), /*14.0*/ + HB_SCRIPT_VITHKUQI = HB_TAG ('V','i','t','h'), /*14.0*/ + + /* + * Since 3.4.0 + */ + HB_SCRIPT_MATH = HB_TAG ('Z','m','t','h'), + + /* + * Since 5.2.0 + */ + HB_SCRIPT_KAWI = HB_TAG ('K','a','w','i'), /*15.0*/ + HB_SCRIPT_NAG_MUNDARI = HB_TAG ('N','a','g','m'), /*15.0*/ /* No script set. */ - HB_SCRIPT_INVALID = HB_TAG_NONE, + HB_SCRIPT_INVALID = HB_TAG_NONE, + + /*< private >*/ /* Dummy values to ensure any hb_tag_t value can be passed/stored as hb_script_t - * without risking undefined behavior. Include both a signed and unsigned max, - * since technically enums are int, and indeed, hb_script_t ends up being signed. + * without risking undefined behavior. We have two, for historical reasons. + * HB_TAG_MAX used to be unsigned, but that was invalid Ansi C, so was changed + * to _HB_SCRIPT_MAX_VALUE to be equal to HB_TAG_MAX_SIGNED as well. + * * See this thread for technicalities: * - * http://lists.freedesktop.org/archives/harfbuzz/2014-March/004150.html + * https://lists.freedesktop.org/archives/harfbuzz/2014-March/004150.html */ - _HB_SCRIPT_MAX_VALUE = HB_TAG_MAX, /*< skip >*/ + _HB_SCRIPT_MAX_VALUE = HB_TAG_MAX_SIGNED, /*< skip >*/ _HB_SCRIPT_MAX_VALUE_SIGNED = HB_TAG_MAX_SIGNED /*< skip >*/ } hb_script_t; @@ -348,16 +771,64 @@ hb_script_get_horizontal_direction (hb_script_t script); /* User data */ +/** + * hb_user_data_key_t: + * + * Data structure for holding user-data keys. + * + **/ typedef struct hb_user_data_key_t { /*< private >*/ char unused; } hb_user_data_key_t; +/** + * hb_destroy_func_t: + * @user_data: the data to be destroyed + * + * A virtual method for destroy user-data callbacks. + * + */ typedef void (*hb_destroy_func_t) (void *user_data); /* Font features and variations. */ +/** + * HB_FEATURE_GLOBAL_START: + * + * Special setting for #hb_feature_t.start to apply the feature from the start + * of the buffer. + * + * Since: 2.0.0 + */ +#define HB_FEATURE_GLOBAL_START 0 + +/** + * HB_FEATURE_GLOBAL_END: + * + * Special setting for #hb_feature_t.end to apply the feature from to the end + * of the buffer. + * + * Since: 2.0.0 + */ +#define HB_FEATURE_GLOBAL_END ((unsigned int) -1) + +/** + * hb_feature_t: + * @tag: The #hb_tag_t tag of the feature + * @value: The value of the feature. 0 disables the feature, non-zero (usually + * 1) enables the feature. For features implemented as lookup type 3 (like + * 'salt') the @value is a one based index into the alternates. + * @start: the cluster to start applying this feature setting (inclusive). + * @end: the cluster to end applying this feature setting (exclusive). + * + * The #hb_feature_t is the structure that holds information about requested + * feature application. The feature will be applied with the given value to all + * glyphs which are in clusters between @start (inclusive) and @end (exclusive). + * Setting start to #HB_FEATURE_GLOBAL_START and end to #HB_FEATURE_GLOBAL_END + * specifies that the feature always applies to the entire buffer. + */ typedef struct hb_feature_t { hb_tag_t tag; uint32_t value; @@ -375,7 +846,13 @@ hb_feature_to_string (hb_feature_t *feature, /** * hb_variation_t: + * @tag: The #hb_tag_t tag of the variation-axis name + * @value: The value of the variation axis * + * Data type for holding variation data. Registered OpenType + * variation-axis tags are listed in + * [OpenType Axis Tag Registry](https://docs.microsoft.com/en-us/typography/opentype/spec/dvaraxisreg). + * * Since: 1.4.2 */ typedef struct hb_variation_t { @@ -391,6 +868,70 @@ HB_EXTERN void hb_variation_to_string (hb_variation_t *variation, char *buf, unsigned int size); +/** + * hb_color_t: + * + * Data type for holding color values. Colors are eight bits per + * channel RGB plus alpha transparency. + * + * Since: 2.1.0 + */ +typedef uint32_t hb_color_t; + +/** + * HB_COLOR: + * @b: blue channel value + * @g: green channel value + * @r: red channel value + * @a: alpha channel value + * + * Constructs an #hb_color_t from four integers. + * + * Since: 2.1.0 + */ +#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_t color); +#define hb_color_get_alpha(color) ((color) & 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_glyph_extents_t: + * @x_bearing: Distance from the x-origin to the left extremum of the glyph. + * @y_bearing: Distance from the top extremum of the glyph to the y-origin. + * @width: Distance from the left extremum of the glyph to the right extremum. + * @height: Distance from the top extremum of the glyph to the bottom extremum. + * + * Glyph extent values, measured in font units. + * + * Note that @height is negative, in coordinate systems that grow up. + **/ +typedef struct hb_glyph_extents_t { + hb_position_t x_bearing; + hb_position_t y_bearing; + hb_position_t width; + hb_position_t height; +} hb_glyph_extents_t; + +/** + * hb_font_t: + * + * Data type for holding fonts. + * + */ +typedef struct hb_font_t hb_font_t; HB_END_DECLS diff --git a/src/3rdparty/harfbuzz-ng/src/hb-config.hh b/src/3rdparty/harfbuzz-ng/src/hb-config.hh new file mode 100644 index 00000000000..816c55c7d36 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-config.hh @@ -0,0 +1,208 @@ +/* + * Copyright © 2019 Facebook, 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. + * + * Facebook Author(s): Behdad Esfahbod + */ + +#ifndef HB_CONFIG_HH +#define HB_CONFIG_HH + +#if 0 /* Make test happy. */ +#include "hb.hh" +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef HB_EXPERIMENTAL_API +#define HB_NO_BEYOND_64K +#define HB_NO_CUBIC_GLYF +#define HB_NO_VAR_COMPOSITES +#endif + +#ifdef HB_TINY +#define HB_LEAN +#define HB_MINI +#define HB_OPTIMIZE_SIZE +#define HB_OPTIMIZE_SIZE_MORE +#define HB_MINIMIZE_MEMORY_USAGE +#define HB_NO_MT +#define HB_NO_UCD_UNASSIGNED +#ifndef NDEBUG +#define NDEBUG +#endif +#endif + +#ifdef HB_LEAN +#define HB_DISABLE_DEPRECATED +#define HB_NDEBUG +#define HB_NO_ATEXIT +#define HB_NO_BUFFER_MESSAGE +#define HB_NO_BUFFER_SERIALIZE +#define HB_NO_BUFFER_VERIFY +#define HB_NO_BITMAP +#define HB_NO_CFF +#define HB_NO_COLOR +#define HB_NO_DRAW +#define HB_NO_ERRNO +#define HB_NO_FACE_COLLECT_UNICODES +#define HB_NO_GETENV +#define HB_NO_HINTING +#define HB_NO_LANGUAGE_LONG +#define HB_NO_LANGUAGE_PRIVATE_SUBTAG +#define HB_NO_LAYOUT_FEATURE_PARAMS +#define HB_NO_LAYOUT_COLLECT_GLYPHS +#define HB_NO_LAYOUT_RARELY_USED +#define HB_NO_LAYOUT_UNUSED +#define HB_NO_MATH +#define HB_NO_META +#define HB_NO_METRICS +#define HB_NO_MMAP +#define HB_NO_NAME +#define HB_NO_OPEN +#define HB_NO_OT_FONT_GLYPH_NAMES +#define HB_NO_OT_SHAPE_FRACTIONS +#define HB_NO_PAINT +#define HB_NO_SETLOCALE +#define HB_NO_STYLE +#define HB_NO_SUBSET_LAYOUT +#define HB_NO_VERTICAL +#define HB_NO_VAR +#endif + +#ifdef HB_MINI +#define HB_NO_AAT +#define HB_NO_LEGACY +#define HB_NO_BORING_EXPANSION +#endif + +#ifdef __OPTIMIZE_SIZE__ +#ifndef HB_OPTIMIZE_SIZE +#define HB_OPTIMIZE_SIZE +#endif +#endif + +#if defined(HAVE_CONFIG_OVERRIDE_H) || defined(HB_CONFIG_OVERRIDE_H) +#ifndef HB_CONFIG_OVERRIDE_H +#define HB_CONFIG_OVERRIDE_H "config-override.h" +#endif +#include HB_CONFIG_OVERRIDE_H +#endif + +/* Closure of options. */ + +#ifdef HB_NO_BORING_EXPANSION +#define HB_NO_BEYOND_64K +#define HB_NO_CUBIC_GLYF +#define HB_NO_VAR_COMPOSITES +#endif + +#ifdef HB_DISABLE_DEPRECATED +#define HB_IF_NOT_DEPRECATED(x) +#else +#define HB_IF_NOT_DEPRECATED(x) x +#endif + +#ifdef HB_NO_SHAPER +#define HB_NO_OT_SHAPE +#define HB_NO_AAT_SHAPE +#endif + +#ifdef HB_NO_AAT +#define HB_NO_OT_NAME_LANGUAGE_AAT +#define HB_NO_AAT_SHAPE +#endif + +#ifdef HB_NO_BITMAP +#define HB_NO_OT_FONT_BITMAP +#endif + +#ifdef HB_NO_CFF +#define HB_NO_OT_FONT_CFF +#define HB_NO_SUBSET_CFF +#endif + +#ifdef HB_NO_DRAW +#define HB_NO_OUTLINE +#endif + +#ifdef HB_NO_GETENV +#define HB_NO_UNISCRIBE_BUG_COMPATIBLE +#endif + +#ifdef HB_NO_LEGACY +#define HB_NO_CMAP_LEGACY_SUBTABLES +#define HB_NO_FALLBACK_SHAPE +#define HB_NO_OT_KERN +#define HB_NO_OT_LAYOUT_BLOCKLIST +#define HB_NO_OT_SHAPE_FALLBACK +#endif + +#ifdef HB_NO_NAME +#define HB_NO_OT_NAME_LANGUAGE +#endif + +#ifdef HB_NO_OT +#define HB_NO_OT_FONT +#define HB_NO_OT_LAYOUT +#define HB_NO_OT_TAG +#define HB_NO_OT_SHAPE +#endif + +#ifdef HB_NO_OT_SHAPE +#define HB_NO_AAT_SHAPE +#endif + +#ifdef HB_NO_OT_SHAPE_FALLBACK +#define HB_NO_OT_SHAPER_ARABIC_FALLBACK +#define HB_NO_OT_SHAPER_HEBREW_FALLBACK +#define HB_NO_OT_SHAPER_THAI_FALLBACK +#define HB_NO_OT_SHAPER_VOWEL_CONSTRAINTS +#define HB_NO_OT_SHAPER_MYANMAR_ZAWGYI +#endif + +#ifdef HB_OPTIMIZE_SIZE_MORE +#define HB_NO_OT_RULESETS_FAST_PATH +#endif + +#ifdef HB_MINIMIZE_MEMORY_USAGE +#define HB_NO_GDEF_CACHE +#define HB_NO_OT_LAYOUT_LOOKUP_CACHE +#define HB_NO_OT_FONT_ADVANCE_CACHE +#define HB_NO_OT_FONT_CMAP_CACHE +#endif + +#ifdef HB_OPTIMIZE_SIZE +#define HB_OPTIMIZE_SIZE_VAL 1 +#else +#define HB_OPTIMIZE_SIZE_VAL 0 +#endif + +#ifdef HB_MINIMIZE_MEMORY_USAGE +#define HB_MINIMIZE_MEMORY_USAGE_VAL 1 +#else +#define HB_MINIMIZE_MEMORY_USAGE_VAL 0 +#endif + +#endif /* HB_CONFIG_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-coretext.mm b/src/3rdparty/harfbuzz-ng/src/hb-coretext.cc similarity index 58% rename from src/3rdparty/harfbuzz-ng/src/hb-coretext.mm rename to src/3rdparty/harfbuzz-ng/src/hb-coretext.cc index 185f73cd681..a87cb5cd028 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-coretext.mm +++ b/src/3rdparty/harfbuzz-ng/src/hb-coretext.cc @@ -26,29 +26,24 @@ * Google Author(s): Behdad Esfahbod */ -#define HB_SHAPER coretext +#include "hb.hh" -#include "hb-private.hh" -#include "hb-debug.hh" -#include "hb-shaper-impl-private.hh" +#ifdef HAVE_CORETEXT + +#include "hb-shaper-impl.hh" #include "hb-coretext.h" -#include +#include "hb-aat-layout.hh" -#include - -typedef bool (*qt_get_font_table_func_t) (void *user_data, unsigned int tag, unsigned char *buffer, unsigned int *length); - -struct FontEngineFaceData { - void *user_data; - qt_get_font_table_func_t get_font_table; -}; - -struct CoreTextFontEngineData { - CTFontRef ctFont; - CGFontRef cgFont; -}; +/** + * SECTION:hb-coretext + * @title: hb-coretext + * @short_description: CoreText integration + * @include: hb-coretext.h + * + * Functions for using HarfBuzz with the CoreText fonts. + **/ /* https://developer.apple.com/documentation/coretext/1508745-ctfontcreatewithgraphicsfont */ #define HB_CORETEXT_DEFAULT_FONT_SIZE 12.f @@ -61,7 +56,7 @@ release_table_data (void *user_data) } static hb_blob_t * -reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) +_hb_cg_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) { CGFontRef cg_font = reinterpret_cast (user_data); CFDataRef cf_data = CGFontCopyTableForTag (cg_font, tag); @@ -71,7 +66,10 @@ reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) const char *data = reinterpret_cast (CFDataGetBytePtr (cf_data)); const size_t length = CFDataGetLength (cf_data); if (!data || !length) + { + CFRelease (cf_data); return nullptr; + } return hb_blob_create (data, length, HB_MEMORY_MODE_READONLY, reinterpret_cast (const_cast<__CFData *> (cf_data)), @@ -85,13 +83,8 @@ _hb_cg_font_release (void *data) } -HB_SHAPER_DATA_ENSURE_DEFINE(coretext, face) -HB_SHAPER_DATA_ENSURE_DEFINE_WITH_CONDITION(coretext, font, - fabs (CTFontGetSize ((CTFontRef) data) - font->ptem) <= .5 -) - static CTFontDescriptorRef -get_last_resort_font_desc (void) +get_last_resort_font_desc () { // TODO Handle allocation failures? CTFontDescriptorRef last_resort = CTFontDescriptorCreateWithNameAndSize (CFSTR("LastResort"), 0); @@ -117,7 +110,7 @@ static void release_data (void *info, const void *data, size_t size) { assert (hb_blob_get_length ((hb_blob_t *) info) == size && - hb_blob_get_data ((hb_blob_t *) info, nullptr) == data); + hb_blob_get_data ((hb_blob_t *) info, nullptr) == data); hb_blob_destroy ((hb_blob_t *) info); } @@ -132,7 +125,6 @@ create_cg_font (hb_face_t *face) } else { -#if 0 hb_blob_t *blob = hb_face_reference_blob (face); unsigned int blob_length; const char *blob_data = hb_blob_get_data (blob, &blob_length); @@ -147,11 +139,6 @@ create_cg_font (hb_face_t *face) DEBUG_MSG (CORETEXT, face, "Face CGFontCreateWithDataProvider() failed"); CGDataProviderRelease (provider); } -#else - FontEngineFaceData *fontEngineFaceData = (FontEngineFaceData *) face->user_data; - CoreTextFontEngineData *coreTextFontEngineData = (CoreTextFontEngineData *) fontEngineFaceData->user_data; - cg_font = CGFontRetain (coreTextFontEngineData->cgFont); -#endif } return cg_font; } @@ -168,6 +155,10 @@ create_ct_font (CGFontRef cg_font, CGFloat font_size) if (CFStringHasPrefix (cg_postscript_name, CFSTR (".SFNSText")) || CFStringHasPrefix (cg_postscript_name, CFSTR (".SFNSDisplay"))) { +#if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1080 +# define kCTFontUIFontSystem kCTFontSystemFontType +# define kCTFontUIFontEmphasizedSystem kCTFontEmphasizedSystemFontType +#endif CTFontUIFontType font_type = kCTFontUIFontSystem; if (CFStringHasSuffix (cg_postscript_name, CFSTR ("-Bold"))) font_type = kCTFontUIFontEmphasizedSystem; @@ -198,7 +189,10 @@ create_ct_font (CGFontRef cg_font, CGFloat font_size) * reconfiguring the cascade list causes CoreText crashes. For details, see * crbug.com/549610 */ // 0x00070000 stands for "kCTVersionNumber10_10", see CoreText.h +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" if (&CTGetCoreTextVersion != nullptr && CTGetCoreTextVersion() < 0x00070000) { +#pragma GCC diagnostic pop CFStringRef fontName = CTFontCopyPostScriptName (ct_font); bool isEmojiFont = CFStringCompare (fontName, CFSTR("AppleColorEmoji"), 0) == kCFCompareEqualTo; CFRelease (fontName); @@ -206,7 +200,18 @@ create_ct_font (CGFontRef cg_font, CGFloat font_size) return ct_font; } - CFURLRef original_url = (CFURLRef)CTFontCopyAttribute(ct_font, kCTFontURLAttribute); + CFURLRef original_url = nullptr; +#if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1060 + ATSFontRef atsFont; + FSRef fsref; + OSStatus status; + atsFont = CTFontGetPlatformFont (ct_font, NULL); + status = ATSFontGetFileReference (atsFont, &fsref); + if (status == noErr) + original_url = CFURLCreateFromFSRef (NULL, &fsref); +#else + original_url = (CFURLRef) CTFontCopyAttribute (ct_font, kCTFontURLAttribute); +#endif /* Create font copy with cascade list that has LastResort first; this speeds up CoreText * font fallback which we don't need anyway. */ @@ -225,18 +230,26 @@ create_ct_font (CGFontRef cg_font, CGFloat font_size) * system locations that we cannot access from the sandboxed renderer * process in Blink. This can be detected by the new file URL location * that the newly found font points to. */ - CFURLRef new_url = (CFURLRef) CTFontCopyAttribute (new_ct_font, kCTFontURLAttribute); + CFURLRef new_url = nullptr; +#if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1060 + atsFont = CTFontGetPlatformFont (new_ct_font, NULL); + status = ATSFontGetFileReference (atsFont, &fsref); + if (status == noErr) + new_url = CFURLCreateFromFSRef (NULL, &fsref); +#else + new_url = (CFURLRef) CTFontCopyAttribute (new_ct_font, kCTFontURLAttribute); +#endif // Keep reconfigured font if URL cannot be retrieved (seems to be the case // on Mac OS 10.12 Sierra), speculative fix for crbug.com/625606 if (!original_url || !new_url || CFEqual (original_url, new_url)) { - CFRelease (ct_font); - ct_font = new_ct_font; + CFRelease (ct_font); + ct_font = new_ct_font; } else { - CFRelease (new_ct_font); - DEBUG_MSG (CORETEXT, ct_font, "Discarding reconfigured CTFont, location changed."); + CFRelease (new_ct_font); + DEBUG_MSG (CORETEXT, ct_font, "Discarding reconfigured CTFont, location changed."); } if (new_url) - CFRelease (new_url); + CFRelease (new_url); } else DEBUG_MSG (CORETEXT, ct_font, "Font copy with empty cascade list failed"); @@ -247,7 +260,7 @@ create_ct_font (CGFontRef cg_font, CGFloat font_size) return ct_font; } -hb_coretext_shaper_face_data_t * +hb_coretext_face_data_t * _hb_coretext_shaper_face_data_create (hb_face_t *face) { CGFontRef cg_font = create_cg_font (face); @@ -258,40 +271,59 @@ _hb_coretext_shaper_face_data_create (hb_face_t *face) return nullptr; } - return (hb_coretext_shaper_face_data_t *) cg_font; + return (hb_coretext_face_data_t *) cg_font; } void -_hb_coretext_shaper_face_data_destroy (hb_coretext_shaper_face_data_t *data) +_hb_coretext_shaper_face_data_destroy (hb_coretext_face_data_t *data) { CFRelease ((CGFontRef) data); } +/** + * hb_coretext_face_create: + * @cg_font: The CGFontRef to work upon + * + * Creates an #hb_face_t face object from the specified + * CGFontRef. + * + * Return value: the new #hb_face_t face object + * + * Since: 0.9.10 + */ hb_face_t * hb_coretext_face_create (CGFontRef cg_font) { - return hb_face_create_for_tables (reference_table, CGFontRetain (cg_font), _hb_cg_font_release); + return hb_face_create_for_tables (_hb_cg_reference_table, CGFontRetain (cg_font), _hb_cg_font_release); } -/* +/** + * hb_coretext_face_get_cg_font: + * @face: The #hb_face_t to work upon + * + * Fetches the CGFontRef associated with an #hb_face_t + * face object + * + * Return value: the CGFontRef found + * * Since: 0.9.10 */ CGFontRef hb_coretext_face_get_cg_font (hb_face_t *face) { - if (unlikely (!hb_coretext_shaper_face_data_ensure (face))) return nullptr; - return (CGFontRef) HB_SHAPER_DATA_GET (face); + return (CGFontRef) (const void *) face->data.coretext; } -hb_coretext_shaper_font_data_t * +hb_coretext_font_data_t * _hb_coretext_shaper_font_data_create (hb_font_t *font) { hb_face_t *face = font->face; - if (unlikely (!hb_coretext_shaper_face_data_ensure (face))) return nullptr; - CGFontRef cg_font = (CGFontRef) HB_SHAPER_DATA_GET (face); + const hb_coretext_face_data_t *face_data = face->data.coretext; + if (unlikely (!face_data)) return nullptr; + CGFontRef cg_font = (CGFontRef) (const void *) face->data.coretext; - CGFloat font_size = font->ptem <= 0.f ? HB_CORETEXT_DEFAULT_FONT_SIZE : font->ptem; + CGFloat font_size = (CGFloat) (font->ptem <= 0.f ? HB_CORETEXT_DEFAULT_FONT_SIZE : font->ptem); CTFontRef ct_font = create_ct_font (cg_font, font_size); if (unlikely (!ct_font)) @@ -300,66 +332,103 @@ _hb_coretext_shaper_font_data_create (hb_font_t *font) return nullptr; } - return (hb_coretext_shaper_font_data_t *) ct_font; + if (font->num_coords) + { + CFMutableDictionaryRef variations = + CFDictionaryCreateMutable (kCFAllocatorDefault, + font->num_coords, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + for (unsigned i = 0; i < font->num_coords; i++) + { + if (font->coords[i] == 0.) continue; + + hb_ot_var_axis_info_t info; + unsigned int c = 1; + hb_ot_var_get_axis_infos (font->face, i, &c, &info); + float v = hb_clamp (font->design_coords[i], info.min_value, info.max_value); + + CFNumberRef tag_number = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &info.tag); + CFNumberRef value_number = CFNumberCreate (kCFAllocatorDefault, kCFNumberFloatType, &v); + CFDictionarySetValue (variations, tag_number, value_number); + CFRelease (tag_number); + CFRelease (value_number); + } + + CFDictionaryRef attributes = + CFDictionaryCreate (kCFAllocatorDefault, + (const void **) &kCTFontVariationAttribute, + (const void **) &variations, + 1, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + CTFontDescriptorRef varDesc = CTFontDescriptorCreateWithAttributes (attributes); + CTFontRef new_ct_font = CTFontCreateCopyWithAttributes (ct_font, 0, nullptr, varDesc); + + CFRelease (ct_font); + CFRelease (attributes); + CFRelease (variations); + ct_font = new_ct_font; + } + + return (hb_coretext_font_data_t *) ct_font; } void -_hb_coretext_shaper_font_data_destroy (hb_coretext_shaper_font_data_t *data) +_hb_coretext_shaper_font_data_destroy (hb_coretext_font_data_t *data) { CFRelease ((CTFontRef) data); } -/* +/** + * hb_coretext_font_create: + * @ct_font: The CTFontRef to work upon + * + * Creates an #hb_font_t font object from the specified + * CTFontRef. + * + * Return value: the new #hb_font_t font object + * * Since: 1.7.2 - */ + **/ hb_font_t * hb_coretext_font_create (CTFontRef ct_font) { - CGFontRef cg_font = CTFontCopyGraphicsFont (ct_font, 0); + CGFontRef cg_font = CTFontCopyGraphicsFont (ct_font, nullptr); hb_face_t *face = hb_coretext_face_create (cg_font); CFRelease (cg_font); hb_font_t *font = hb_font_create (face); hb_face_destroy (face); - if (unlikely (hb_object_is_inert (font))) + if (unlikely (hb_object_is_immutable (font))) return font; hb_font_set_ptem (font, CTFontGetSize (ct_font)); /* Let there be dragons here... */ - HB_SHAPER_DATA_GET (font) = (hb_coretext_shaper_font_data_t *) CFRetain (ct_font); + font->data.coretext.cmpexch (nullptr, (hb_coretext_font_data_t *) CFRetain (ct_font)); return font; } +/** + * hb_coretext_font_get_ct_font: + * @font: #hb_font_t to work upon + * + * Fetches the CTFontRef associated with the specified + * #hb_font_t font object. + * + * Return value: the CTFontRef found + * + * Since: 0.9.10 + */ CTFontRef hb_coretext_font_get_ct_font (hb_font_t *font) { - if (unlikely (!hb_coretext_shaper_font_data_ensure (font))) return nullptr; - return (CTFontRef) HB_SHAPER_DATA_GET (font); -} - - - -/* - * shaper shape_plan data - */ - -struct hb_coretext_shaper_shape_plan_data_t {}; - -hb_coretext_shaper_shape_plan_data_t * -_hb_coretext_shaper_shape_plan_data_create (hb_shape_plan_t *shape_plan HB_UNUSED, - const hb_feature_t *user_features HB_UNUSED, - unsigned int num_user_features HB_UNUSED, - const int *coords HB_UNUSED, - unsigned int num_coords HB_UNUSED) -{ - return (hb_coretext_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED; -} - -void -_hb_coretext_shaper_shape_plan_data_destroy (hb_coretext_shaper_shape_plan_data_t *data HB_UNUSED) -{ + CTFontRef ct_font = (CTFontRef) (const void *) font->data.coretext; + return ct_font ? (CTFontRef) ct_font : nullptr; } @@ -376,7 +445,7 @@ struct active_feature_t { feature_record_t rec; unsigned int order; - static int cmp (const void *pa, const void *pb) { + HB_INTERNAL static int cmp (const void *pa, const void *pb) { const active_feature_t *a = (const active_feature_t *) pa; const active_feature_t *b = (const active_feature_t *) pb; return a->rec.feature < b->rec.feature ? -1 : a->rec.feature > b->rec.feature ? 1 : @@ -384,8 +453,8 @@ struct active_feature_t { a->rec.setting < b->rec.setting ? -1 : a->rec.setting > b->rec.setting ? 1 : 0; } - bool operator== (const active_feature_t *f) { - return cmp (this, f) == 0; + bool operator== (const active_feature_t& f) const { + return cmp (this, &f) == 0; } }; @@ -394,7 +463,7 @@ struct feature_event_t { bool start; active_feature_t feature; - static int cmp (const void *pa, const void *pb) { + HB_INTERNAL static int cmp (const void *pa, const void *pb) { const feature_event_t *a = (const feature_event_t *) pa; const feature_event_t *b = (const feature_event_t *) pb; return a->index < b->index ? -1 : a->index > b->index ? 1 : @@ -410,200 +479,23 @@ struct range_record_t { }; -/* The following enum members are added in OS X 10.8. */ -#define kAltHalfWidthTextSelector 6 -#define kAltProportionalTextSelector 5 -#define kAlternateHorizKanaOffSelector 1 -#define kAlternateHorizKanaOnSelector 0 -#define kAlternateKanaType 34 -#define kAlternateVertKanaOffSelector 3 -#define kAlternateVertKanaOnSelector 2 -#define kCaseSensitiveLayoutOffSelector 1 -#define kCaseSensitiveLayoutOnSelector 0 -#define kCaseSensitiveLayoutType 33 -#define kCaseSensitiveSpacingOffSelector 3 -#define kCaseSensitiveSpacingOnSelector 2 -#define kContextualAlternatesOffSelector 1 -#define kContextualAlternatesOnSelector 0 -#define kContextualAlternatesType 36 -#define kContextualLigaturesOffSelector 19 -#define kContextualLigaturesOnSelector 18 -#define kContextualSwashAlternatesOffSelector 5 -#define kContextualSwashAlternatesOnSelector 4 -#define kDefaultLowerCaseSelector 0 -#define kDefaultUpperCaseSelector 0 -#define kHistoricalLigaturesOffSelector 21 -#define kHistoricalLigaturesOnSelector 20 -#define kHojoCharactersSelector 12 -#define kJIS2004CharactersSelector 11 -#define kLowerCasePetiteCapsSelector 2 -#define kLowerCaseSmallCapsSelector 1 -#define kLowerCaseType 37 -#define kMathematicalGreekOffSelector 11 -#define kMathematicalGreekOnSelector 10 -#define kNLCCharactersSelector 13 -#define kQuarterWidthTextSelector 4 -#define kScientificInferiorsSelector 4 -#define kStylisticAltEightOffSelector 17 -#define kStylisticAltEightOnSelector 16 -#define kStylisticAltEighteenOffSelector 37 -#define kStylisticAltEighteenOnSelector 36 -#define kStylisticAltElevenOffSelector 23 -#define kStylisticAltElevenOnSelector 22 -#define kStylisticAltFifteenOffSelector 31 -#define kStylisticAltFifteenOnSelector 30 -#define kStylisticAltFiveOffSelector 11 -#define kStylisticAltFiveOnSelector 10 -#define kStylisticAltFourOffSelector 9 -#define kStylisticAltFourOnSelector 8 -#define kStylisticAltFourteenOffSelector 29 -#define kStylisticAltFourteenOnSelector 28 -#define kStylisticAltNineOffSelector 19 -#define kStylisticAltNineOnSelector 18 -#define kStylisticAltNineteenOffSelector 39 -#define kStylisticAltNineteenOnSelector 38 -#define kStylisticAltOneOffSelector 3 -#define kStylisticAltOneOnSelector 2 -#define kStylisticAltSevenOffSelector 15 -#define kStylisticAltSevenOnSelector 14 -#define kStylisticAltSeventeenOffSelector 35 -#define kStylisticAltSeventeenOnSelector 34 -#define kStylisticAltSixOffSelector 13 -#define kStylisticAltSixOnSelector 12 -#define kStylisticAltSixteenOffSelector 33 -#define kStylisticAltSixteenOnSelector 32 -#define kStylisticAltTenOffSelector 21 -#define kStylisticAltTenOnSelector 20 -#define kStylisticAltThirteenOffSelector 27 -#define kStylisticAltThirteenOnSelector 26 -#define kStylisticAltThreeOffSelector 7 -#define kStylisticAltThreeOnSelector 6 -#define kStylisticAltTwelveOffSelector 25 -#define kStylisticAltTwelveOnSelector 24 -#define kStylisticAltTwentyOffSelector 41 -#define kStylisticAltTwentyOnSelector 40 -#define kStylisticAltTwoOffSelector 5 -#define kStylisticAltTwoOnSelector 4 -#define kStylisticAlternativesType 35 -#define kSwashAlternatesOffSelector 3 -#define kSwashAlternatesOnSelector 2 -#define kThirdWidthTextSelector 3 -#define kTraditionalNamesCharactersSelector 14 -#define kUpperCasePetiteCapsSelector 2 -#define kUpperCaseSmallCapsSelector 1 -#define kUpperCaseType 38 - -/* Table data courtesy of Apple. */ -static const struct feature_mapping_t { - FourCharCode otFeatureTag; - uint16_t aatFeatureType; - uint16_t selectorToEnable; - uint16_t selectorToDisable; -} feature_mappings[] = { - { 'c2pc', kUpperCaseType, kUpperCasePetiteCapsSelector, kDefaultUpperCaseSelector }, - { 'c2sc', kUpperCaseType, kUpperCaseSmallCapsSelector, kDefaultUpperCaseSelector }, - { 'calt', kContextualAlternatesType, kContextualAlternatesOnSelector, kContextualAlternatesOffSelector }, - { 'case', kCaseSensitiveLayoutType, kCaseSensitiveLayoutOnSelector, kCaseSensitiveLayoutOffSelector }, - { 'clig', kLigaturesType, kContextualLigaturesOnSelector, kContextualLigaturesOffSelector }, - { 'cpsp', kCaseSensitiveLayoutType, kCaseSensitiveSpacingOnSelector, kCaseSensitiveSpacingOffSelector }, - { 'cswh', kContextualAlternatesType, kContextualSwashAlternatesOnSelector, kContextualSwashAlternatesOffSelector }, - { 'dlig', kLigaturesType, kRareLigaturesOnSelector, kRareLigaturesOffSelector }, - { 'expt', kCharacterShapeType, kExpertCharactersSelector, 16 }, - { 'frac', kFractionsType, kDiagonalFractionsSelector, kNoFractionsSelector }, - { 'fwid', kTextSpacingType, kMonospacedTextSelector, 7 }, - { 'halt', kTextSpacingType, kAltHalfWidthTextSelector, 7 }, - { 'hist', kLigaturesType, kHistoricalLigaturesOnSelector, kHistoricalLigaturesOffSelector }, - { 'hkna', kAlternateKanaType, kAlternateHorizKanaOnSelector, kAlternateHorizKanaOffSelector, }, - { 'hlig', kLigaturesType, kHistoricalLigaturesOnSelector, kHistoricalLigaturesOffSelector }, - { 'hngl', kTransliterationType, kHanjaToHangulSelector, kNoTransliterationSelector }, - { 'hojo', kCharacterShapeType, kHojoCharactersSelector, 16 }, - { 'hwid', kTextSpacingType, kHalfWidthTextSelector, 7 }, - { 'ital', kItalicCJKRomanType, kCJKItalicRomanOnSelector, kCJKItalicRomanOffSelector }, - { 'jp04', kCharacterShapeType, kJIS2004CharactersSelector, 16 }, - { 'jp78', kCharacterShapeType, kJIS1978CharactersSelector, 16 }, - { 'jp83', kCharacterShapeType, kJIS1983CharactersSelector, 16 }, - { 'jp90', kCharacterShapeType, kJIS1990CharactersSelector, 16 }, - { 'liga', kLigaturesType, kCommonLigaturesOnSelector, kCommonLigaturesOffSelector }, - { 'lnum', kNumberCaseType, kUpperCaseNumbersSelector, 2 }, - { 'mgrk', kMathematicalExtrasType, kMathematicalGreekOnSelector, kMathematicalGreekOffSelector }, - { 'nlck', kCharacterShapeType, kNLCCharactersSelector, 16 }, - { 'onum', kNumberCaseType, kLowerCaseNumbersSelector, 2 }, - { 'ordn', kVerticalPositionType, kOrdinalsSelector, kNormalPositionSelector }, - { 'palt', kTextSpacingType, kAltProportionalTextSelector, 7 }, - { 'pcap', kLowerCaseType, kLowerCasePetiteCapsSelector, kDefaultLowerCaseSelector }, - { 'pkna', kTextSpacingType, kProportionalTextSelector, 7 }, - { 'pnum', kNumberSpacingType, kProportionalNumbersSelector, 4 }, - { 'pwid', kTextSpacingType, kProportionalTextSelector, 7 }, - { 'qwid', kTextSpacingType, kQuarterWidthTextSelector, 7 }, - { 'ruby', kRubyKanaType, kRubyKanaOnSelector, kRubyKanaOffSelector }, - { 'sinf', kVerticalPositionType, kScientificInferiorsSelector, kNormalPositionSelector }, - { 'smcp', kLowerCaseType, kLowerCaseSmallCapsSelector, kDefaultLowerCaseSelector }, - { 'smpl', kCharacterShapeType, kSimplifiedCharactersSelector, 16 }, - { 'ss01', kStylisticAlternativesType, kStylisticAltOneOnSelector, kStylisticAltOneOffSelector }, - { 'ss02', kStylisticAlternativesType, kStylisticAltTwoOnSelector, kStylisticAltTwoOffSelector }, - { 'ss03', kStylisticAlternativesType, kStylisticAltThreeOnSelector, kStylisticAltThreeOffSelector }, - { 'ss04', kStylisticAlternativesType, kStylisticAltFourOnSelector, kStylisticAltFourOffSelector }, - { 'ss05', kStylisticAlternativesType, kStylisticAltFiveOnSelector, kStylisticAltFiveOffSelector }, - { 'ss06', kStylisticAlternativesType, kStylisticAltSixOnSelector, kStylisticAltSixOffSelector }, - { 'ss07', kStylisticAlternativesType, kStylisticAltSevenOnSelector, kStylisticAltSevenOffSelector }, - { 'ss08', kStylisticAlternativesType, kStylisticAltEightOnSelector, kStylisticAltEightOffSelector }, - { 'ss09', kStylisticAlternativesType, kStylisticAltNineOnSelector, kStylisticAltNineOffSelector }, - { 'ss10', kStylisticAlternativesType, kStylisticAltTenOnSelector, kStylisticAltTenOffSelector }, - { 'ss11', kStylisticAlternativesType, kStylisticAltElevenOnSelector, kStylisticAltElevenOffSelector }, - { 'ss12', kStylisticAlternativesType, kStylisticAltTwelveOnSelector, kStylisticAltTwelveOffSelector }, - { 'ss13', kStylisticAlternativesType, kStylisticAltThirteenOnSelector, kStylisticAltThirteenOffSelector }, - { 'ss14', kStylisticAlternativesType, kStylisticAltFourteenOnSelector, kStylisticAltFourteenOffSelector }, - { 'ss15', kStylisticAlternativesType, kStylisticAltFifteenOnSelector, kStylisticAltFifteenOffSelector }, - { 'ss16', kStylisticAlternativesType, kStylisticAltSixteenOnSelector, kStylisticAltSixteenOffSelector }, - { 'ss17', kStylisticAlternativesType, kStylisticAltSeventeenOnSelector, kStylisticAltSeventeenOffSelector }, - { 'ss18', kStylisticAlternativesType, kStylisticAltEighteenOnSelector, kStylisticAltEighteenOffSelector }, - { 'ss19', kStylisticAlternativesType, kStylisticAltNineteenOnSelector, kStylisticAltNineteenOffSelector }, - { 'ss20', kStylisticAlternativesType, kStylisticAltTwentyOnSelector, kStylisticAltTwentyOffSelector }, - { 'subs', kVerticalPositionType, kInferiorsSelector, kNormalPositionSelector }, - { 'sups', kVerticalPositionType, kSuperiorsSelector, kNormalPositionSelector }, - { 'swsh', kContextualAlternatesType, kSwashAlternatesOnSelector, kSwashAlternatesOffSelector }, - { 'titl', kStyleOptionsType, kTitlingCapsSelector, kNoStyleOptionsSelector }, - { 'tnam', kCharacterShapeType, kTraditionalNamesCharactersSelector, 16 }, - { 'tnum', kNumberSpacingType, kMonospacedNumbersSelector, 4 }, - { 'trad', kCharacterShapeType, kTraditionalCharactersSelector, 16 }, - { 'twid', kTextSpacingType, kThirdWidthTextSelector, 7 }, - { 'unic', kLetterCaseType, 14, 15 }, - { 'valt', kTextSpacingType, kAltProportionalTextSelector, 7 }, - { 'vert', kVerticalSubstitutionType, kSubstituteVerticalFormsOnSelector, kSubstituteVerticalFormsOffSelector }, - { 'vhal', kTextSpacingType, kAltHalfWidthTextSelector, 7 }, - { 'vkna', kAlternateKanaType, kAlternateVertKanaOnSelector, kAlternateVertKanaOffSelector }, - { 'vpal', kTextSpacingType, kAltProportionalTextSelector, 7 }, - { 'vrt2', kVerticalSubstitutionType, kSubstituteVerticalFormsOnSelector, kSubstituteVerticalFormsOffSelector }, - { 'zero', kTypographicExtrasType, kSlashedZeroOnSelector, kSlashedZeroOffSelector }, -}; - -static int -_hb_feature_mapping_cmp (const void *key_, const void *entry_) -{ - unsigned int key = * (unsigned int *) key_; - const feature_mapping_t * entry = (const feature_mapping_t *) entry_; - return key < entry->otFeatureTag ? -1 : - key > entry->otFeatureTag ? 1 : - 0; -} - hb_bool_t _hb_coretext_shape (hb_shape_plan_t *shape_plan, hb_font_t *font, - hb_buffer_t *buffer, - const hb_feature_t *features, - unsigned int num_features) + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features) { hb_face_t *face = font->face; - CGFontRef cg_font = (CGFontRef) HB_SHAPER_DATA_GET (face); - CTFontRef ct_font = (CTFontRef) HB_SHAPER_DATA_GET (font); + CGFontRef cg_font = (CGFontRef) (const void *) face->data.coretext; + CTFontRef ct_font = (CTFontRef) (const void *) font->data.coretext; CGFloat ct_font_size = CTFontGetSize (ct_font); CGFloat x_mult = (CGFloat) font->x_scale / ct_font_size; CGFloat y_mult = (CGFloat) font->y_scale / ct_font_size; /* Attach marks to their bases, to match the 'ot' shaper. - * Adapted from hb-ot-shape:hb_form_clusters(). + * Adapted from a very old version of hb-ot-shape:hb_form_clusters(). * Note that this only makes us be closer to the 'ot' shaper, * but by no means the same. For example, if there's * B1 M1 B2 M2, and B1-B2 form a ligature, M2's cluster will @@ -619,8 +511,7 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, buffer->merge_clusters (i - 1, i + 1); } - hb_auto_array_t feature_records; - hb_auto_array_t range_records; + hb_vector_t range_records; /* * Set up features. @@ -629,34 +520,32 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, if (num_features) { /* Sort features by start/end events. */ - hb_auto_array_t feature_events; + hb_vector_t feature_events; for (unsigned int i = 0; i < num_features; i++) { - const feature_mapping_t * mapping = (const feature_mapping_t *) bsearch (&features[i].tag, - feature_mappings, - ARRAY_LENGTH (feature_mappings), - sizeof (feature_mappings[0]), - _hb_feature_mapping_cmp); - if (!mapping) - continue; - active_feature_t feature; + +#if MAC_OS_X_VERSION_MIN_REQUIRED < 101000 + const hb_aat_feature_mapping_t * mapping = hb_aat_layout_find_feature_mapping (features[i].tag); + if (!mapping) + continue; + feature.rec.feature = mapping->aatFeatureType; feature.rec.setting = features[i].value ? mapping->selectorToEnable : mapping->selectorToDisable; +#else + feature.rec.feature = features[i].tag; + feature.rec.setting = features[i].value; +#endif feature.order = i; feature_event_t *event; event = feature_events.push (); - if (unlikely (!event)) - goto fail_features; event->index = features[i].start; event->start = true; event->feature = feature; event = feature_events.push (); - if (unlikely (!event)) - goto fail_features; event->index = features[i].end; event->start = false; event->feature = feature; @@ -670,35 +559,32 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, feature.order = num_features + 1; feature_event_t *event = feature_events.push (); - if (unlikely (!event)) - goto fail_features; event->index = 0; /* This value does magic. */ event->start = false; event->feature = feature; } /* Scan events and save features for each range. */ - hb_auto_array_t active_features; + hb_vector_t active_features; unsigned int last_index = 0; - for (unsigned int i = 0; i < feature_events.len; i++) + for (unsigned int i = 0; i < feature_events.length; i++) { feature_event_t *event = &feature_events[i]; if (event->index != last_index) { - /* Save a snapshot of active features and the range. */ + /* Save a snapshot of active features and the range. */ range_record_t *range = range_records.push (); - if (unlikely (!range)) - goto fail_features; - if (active_features.len) + if (active_features.length) { CFMutableArrayRef features_array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); /* TODO sort and resolve conflicting features? */ /* active_features.qsort (); */ - for (unsigned int j = 0; j < active_features.len; j++) + for (unsigned int j = 0; j < active_features.length; j++) { +#if MAC_OS_X_VERSION_MIN_REQUIRED < 101000 CFStringRef keys[] = { kCTFontFeatureTypeIdentifierKey, kCTFontFeatureSelectorIdentifierKey @@ -707,6 +593,17 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.feature), CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.setting) }; +#else + char tag[5] = {HB_UNTAG (active_features[j].rec.feature)}; + CFTypeRef keys[] = { + kCTFontOpenTypeFeatureTag, + kCTFontOpenTypeFeatureValue + }; + CFTypeRef values[] = { + CFStringCreateWithCString (kCFAllocatorDefault, tag, kCFStringEncodingASCII), + CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.setting) + }; +#endif static_assert ((ARRAY_LENGTH_CONST (keys) == ARRAY_LENGTH_CONST (values)), ""); CFDictionaryRef dict = CFDictionaryCreate (kCFAllocatorDefault, (const void **) keys, @@ -747,30 +644,23 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, last_index = event->index; } - if (event->start) { - active_feature_t *feature = active_features.push (); - if (unlikely (!feature)) - goto fail_features; - *feature = event->feature; + if (event->start) + { + active_features.push (event->feature); } else { - active_feature_t *feature = active_features.find (&event->feature); + active_feature_t *feature = active_features.lsearch (event->feature); if (feature) - active_features.remove (feature - active_features.array); + active_features.remove_ordered (feature - active_features.arrayZ); } } } - else - { - fail_features: - num_features = 0; - } unsigned int scratch_size; hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size); #define ALLOCATE_ARRAY(Type, name, len, on_no_room) \ Type *name = (Type *) scratch; \ - { \ + do { \ unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \ if (unlikely (_consumed > scratch_size)) \ { \ @@ -779,9 +669,9 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, } \ scratch += _consumed; \ scratch_size -= _consumed; \ - } + } while (0) - ALLOCATE_ARRAY (UniChar, pchars, buffer->len * 2, /*nothing*/); + ALLOCATE_ARRAY (UniChar, pchars, buffer->len * 2, ((void)nullptr) /*nothing*/); unsigned int chars_len = 0; for (unsigned int i = 0; i < buffer->len; i++) { hb_codepoint_t c = buffer->info[i].codepoint; @@ -795,7 +685,7 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, } } - ALLOCATE_ARRAY (unsigned int, log_clusters, chars_len, /*nothing*/); + ALLOCATE_ARRAY (unsigned int, log_clusters, chars_len, ((void)nullptr) /*nothing*/); chars_len = 0; for (unsigned int i = 0; i < buffer->len; i++) { @@ -811,13 +701,13 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, DEBUG_MSG (CORETEXT, nullptr, __VA_ARGS__); \ ret = false; \ goto fail; \ - } HB_STMT_END; + } HB_STMT_END bool ret = true; CFStringRef string_ref = nullptr; CTLineRef line = nullptr; - if (0) + if (false) { resize_and_retry: DEBUG_MSG (CORETEXT, buffer, "Buffer resize"); @@ -873,15 +763,18 @@ resize_and_retry: /* What's the iOS equivalent of this check? * The symbols was introduced in iOS 7.0. * At any rate, our fallback is safe and works fine. */ -#if MAC_OS_X_VERSION_MIN_REQUIRED < 1090 +#if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1090 # define kCTLanguageAttributeName CFSTR ("NSLanguage") #endif - CFStringRef lang = CFStringCreateWithCStringNoCopy (kCFAllocatorDefault, + CFStringRef lang = CFStringCreateWithCStringNoCopy (kCFAllocatorDefault, hb_language_to_string (buffer->props.language), kCFStringEncodingUTF8, kCFAllocatorNull); if (unlikely (!lang)) + { + CFRelease (attr_string); FAIL ("CFStringCreateWithCStringNoCopy failed"); + } CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len), kCTLanguageAttributeName, lang); CFRelease (lang); @@ -889,7 +782,7 @@ resize_and_retry: CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len), kCTFontAttributeName, ct_font); - if (num_features && range_records.len) + if (num_features && range_records.length) { unsigned int start = 0; range_record_t *last_range = &range_records[0]; @@ -930,7 +823,7 @@ resize_and_retry: feature.start < chars_len && feature.start < feature.end) { CFRange feature_range = CFRangeMake (feature.start, - MIN (feature.end, chars_len) - feature.start); + hb_min (feature.end, chars_len) - feature.start); if (feature.value) CFAttributedStringRemoveAttribute (attr_string, feature_range, kCTKernAttributeName); else @@ -942,6 +835,9 @@ resize_and_retry: int level = HB_DIRECTION_IS_FORWARD (buffer->props.direction) ? 0 : 1; CFNumberRef level_number = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &level); +#if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1060 + extern const CFStringRef kCTTypesetterOptionForcedEmbeddingLevel; +#endif CFDictionaryRef options = CFDictionaryCreate (kCFAllocatorDefault, (const void **) &kCTTypesetterOptionForcedEmbeddingLevel, (const void **) &level_number, @@ -950,7 +846,10 @@ resize_and_retry: &kCFTypeDictionaryValueCallBacks); CFRelease (level_number); if (unlikely (!options)) - FAIL ("CFDictionaryCreate failed"); + { + CFRelease (attr_string); + FAIL ("CFDictionaryCreate failed"); + } CTTypesetterRef typesetter = CTTypesetterCreateWithAttributedStringAndOptions (attr_string, options); CFRelease (options); @@ -969,12 +868,12 @@ resize_and_retry: DEBUG_MSG (CORETEXT, nullptr, "Num runs: %d", num_runs); buffer->len = 0; - uint32_t status_and = ~0, status_or = 0; - double advances_so_far = 0; + uint32_t status_or = 0; + CGFloat advances_so_far = 0; /* For right-to-left runs, CoreText returns the glyphs positioned such that * any trailing whitespace is to the left of (0,0). Adjust coordinate system * to fix for that. Test with any RTL string with trailing spaces. - * https://code.google.com/p/chromium/issues/detail?id=469028 + * https://crbug.com/469028 */ if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction)) { @@ -990,12 +889,11 @@ resize_and_retry: CTRunRef run = static_cast(CFArrayGetValueAtIndex (glyph_runs, i)); CTRunStatus run_status = CTRunGetStatus (run); status_or |= run_status; - status_and &= run_status; DEBUG_MSG (CORETEXT, run, "CTRunStatus: %x", run_status); - double run_advance = CTRunGetTypographicBounds (run, range_all, nullptr, nullptr, nullptr); + CGFloat run_advance = CTRunGetTypographicBounds (run, range_all, nullptr, nullptr, nullptr); if (HB_DIRECTION_IS_VERTICAL (buffer->props.direction)) run_advance = -run_advance; - DEBUG_MSG (CORETEXT, run, "Run advance: %g", run_advance); + DEBUG_MSG (CORETEXT, run, "Run advance: %g", (double) run_advance); /* CoreText does automatic font fallback (AKA "cascading") for characters * not supported by the requested font, and provides no way to turn it off, @@ -1027,12 +925,12 @@ resize_and_retry: * However, even that wouldn't work if we were passed in the CGFont to * construct a hb_face to begin with. * - * See: http://github.com/harfbuzz/harfbuzz/pull/36 + * See: https://github.com/harfbuzz/harfbuzz/pull/36 * * Also see: https://bugs.chromium.org/p/chromium/issues/detail?id=597098 */ bool matched = false; - for (unsigned int i = 0; i < range_records.len; i++) + for (unsigned int i = 0; i < range_records.length; i++) if (range_records[i].font && CFEqual (run_ct_font, range_records[i].font)) { matched = true; @@ -1040,7 +938,7 @@ resize_and_retry: } if (!matched) { - CGFontRef run_cg_font = CTFontCopyGraphicsFont (run_ct_font, 0); + CGFontRef run_cg_font = CTFontCopyGraphicsFont (run_ct_font, nullptr); if (run_cg_font) { matched = CFEqual (run_cg_font, cg_font); @@ -1060,7 +958,7 @@ resize_and_retry: if (!matched) { CFRange range = CTRunGetStringRange (run); - DEBUG_MSG (CORETEXT, run, "Run used fallback font: %ld..%ld", + DEBUG_MSG (CORETEXT, run, "Run used fallback font: %ld..%ld", range.location, range.location + range.length); if (!buffer->ensure_inplace (buffer->len + range.length)) goto resize_and_retry; @@ -1088,7 +986,7 @@ resize_and_retry: continue; } if (buffer->unicode->is_default_ignorable (ch)) - continue; + continue; info->codepoint = notdef; info->cluster = log_clusters[j]; @@ -1130,10 +1028,10 @@ resize_and_retry: #define SCRATCH_RESTORE() \ scratch_size = scratch_size_saved; \ - scratch = scratch_saved; + scratch = scratch_saved { /* Setup glyphs */ - SCRATCH_SAVE(); + SCRATCH_SAVE(); const CGGlyph* glyphs = USE_PTR ? CTRunGetGlyphsPtr (run) : nullptr; if (!glyphs) { ALLOCATE_ARRAY (CGGlyph, glyph_buf, num_glyphs, goto resize_and_retry); @@ -1156,12 +1054,12 @@ resize_and_retry: SCRATCH_RESTORE(); } { - /* Setup positions. + /* Setup positions. * Note that CoreText does not return advances for glyphs. As such, * for all but last glyph, we use the delta position to next glyph as * advance (in the advance direction only), and for last glyph we set * whatever is needed to make the whole run's advance add up. */ - SCRATCH_SAVE(); + SCRATCH_SAVE(); const CGPoint* positions = USE_PTR ? CTRunGetPositionsPtr (run) : nullptr; if (!positions) { ALLOCATE_ARRAY (CGPoint, position_buf, num_glyphs, goto resize_and_retry); @@ -1171,32 +1069,34 @@ resize_and_retry: hb_glyph_info_t *info = run_info; if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction)) { - hb_position_t x_offset = (positions[0].x - advances_so_far) * x_mult; + hb_position_t x_offset = round ((positions[0].x - advances_so_far) * x_mult); for (unsigned int j = 0; j < num_glyphs; j++) { - double advance; + CGFloat advance; if (likely (j + 1 < num_glyphs)) advance = positions[j + 1].x - positions[j].x; else /* last glyph */ advance = run_advance - (positions[j].x - positions[0].x); - info->mask = advance * x_mult; + /* int cast necessary to pass through negative values. */ + info->mask = (int) round (advance * x_mult); info->var1.i32 = x_offset; - info->var2.i32 = positions[j].y * y_mult; + info->var2.i32 = round (positions[j].y * y_mult); info++; } } else { - hb_position_t y_offset = (positions[0].y - advances_so_far) * y_mult; + hb_position_t y_offset = round ((positions[0].y - advances_so_far) * y_mult); for (unsigned int j = 0; j < num_glyphs; j++) { - double advance; + CGFloat advance; if (likely (j + 1 < num_glyphs)) advance = positions[j + 1].y - positions[j].y; else /* last glyph */ advance = run_advance - (positions[j].y - positions[0].y); - info->mask = advance * y_mult; - info->var1.i32 = positions[j].x * x_mult; + /* int cast necessary to pass through negative values. */ + info->mask = (int) round (advance * y_mult); + info->var1.i32 = round (positions[j].x * x_mult); info->var2.i32 = y_offset; info++; } @@ -1212,21 +1112,6 @@ resize_and_retry: buffer->len += num_glyphs; } - /* Mac OS 10.6 doesn't have kCTTypesetterOptionForcedEmbeddingLevel, - * or if it does, it doesn't resepct it. So we get runs with wrong - * directions. As such, disable the assert... It wouldn't crash, but - * cursoring will be off... - * - * http://crbug.com/419769 - */ - if (0) - { - /* Make sure all runs had the expected direction. */ - bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); - assert (bool (status_and & kCTRunStatusRightToLeft) == backward); - assert (bool (status_or & kCTRunStatusRightToLeft) == backward); - } - buffer->clear_positions (); unsigned int count = buffer->len; @@ -1239,9 +1124,7 @@ resize_and_retry: pos->x_offset = info->var1.i32; pos->y_offset = info->var2.i32; - info->mask = HB_GLYPH_FLAG_UNSAFE_TO_BREAK; - - info++, pos++; + info++; pos++; } else for (unsigned int i = 0; i < count; i++) @@ -1250,9 +1133,7 @@ resize_and_retry: pos->x_offset = info->var1.i32; pos->y_offset = info->var2.i32; - info->mask = HB_GLYPH_FLAG_UNSAFE_TO_BREAK; - - info++, pos++; + info++; pos++; } /* Fix up clusters so that we never return out-of-order indices; @@ -1265,7 +1146,8 @@ resize_and_retry: * This does *not* mean we'll form the same clusters as Uniscribe * or the native OT backend, only that the cluster indices will be * monotonic in the output buffer. */ - if (count > 1 && (status_or & kCTRunStatusNonMonotonic)) + if (count > 1 && (status_or & kCTRunStatusNonMonotonic) && + buffer->cluster_level != HB_BUFFER_CLUSTER_LEVEL_CHARACTERS) { hb_glyph_info_t *info = buffer->info; if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) @@ -1273,7 +1155,7 @@ resize_and_retry: unsigned int cluster = info[count - 1].cluster; for (unsigned int i = count - 1; i > 0; i--) { - cluster = MIN (cluster, info[i - 1].cluster); + cluster = hb_min (cluster, info[i - 1].cluster); info[i - 1].cluster = cluster; } } @@ -1282,14 +1164,19 @@ resize_and_retry: unsigned int cluster = info[0].cluster; for (unsigned int i = 1; i < count; i++) { - cluster = MIN (cluster, info[i].cluster); + cluster = hb_min (cluster, info[i].cluster); info[i].cluster = cluster; } } } } - buffer->unsafe_to_break_all (); + /* TODO: Sometimes the above positioning code generates negative + * advance values. Fix them up. Example, with NotoNastaliqUrdu + * font and sequence ابهد. */ + + buffer->clear_glyph_flags (); + buffer->unsafe_to_break (); #undef FAIL @@ -1299,7 +1186,7 @@ fail: if (line) CFRelease (line); - for (unsigned int i = 0; i < range_records.len; i++) + for (unsigned int i = 0; i < range_records.length; i++) if (range_records[i].font) CFRelease (range_records[i].font); @@ -1307,108 +1194,4 @@ fail: } -/* - * AAT shaper - */ - -HB_SHAPER_DATA_ENSURE_DEFINE(coretext_aat, face) -HB_SHAPER_DATA_ENSURE_DEFINE(coretext_aat, font) - -/* - * shaper face data - */ - -struct hb_coretext_aat_shaper_face_data_t {}; - -hb_coretext_aat_shaper_face_data_t * -_hb_coretext_aat_shaper_face_data_create (hb_face_t *face) -{ - static const hb_tag_t tags[] = {HB_CORETEXT_TAG_MORX, HB_CORETEXT_TAG_MORT, HB_CORETEXT_TAG_KERX, HB_CORETEXT_TAG_TRAK}; - - // Disable macOS 11 / iOS 14 specific hotfix for Qt on older versions, since it caused a - // regression. (Note: This code should not been upstreamed and is only applicable to Qt 5.15.x). - NSInteger majorVersion = NSProcessInfo.processInfo.operatingSystemVersion.majorVersion; - NSInteger hotfixMinimumVersion; -#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE - hotfixMinimumVersion = 14; -#else - hotfixMinimumVersion = 11; #endif - - unsigned int arrayLength = ARRAY_LENGTH (tags); - if (majorVersion < hotfixMinimumVersion) - arrayLength--; - - for (unsigned int i = 0; i < arrayLength; i++) - { - hb_blob_t *blob = face->reference_table (tags[i]); - if (hb_blob_get_length (blob)) - { - hb_blob_destroy (blob); - return hb_coretext_shaper_face_data_ensure (face) ? (hb_coretext_aat_shaper_face_data_t *) HB_SHAPER_DATA_SUCCEEDED : nullptr; - } - hb_blob_destroy (blob); - } - - return nullptr; -} - -void -_hb_coretext_aat_shaper_face_data_destroy (hb_coretext_aat_shaper_face_data_t *data HB_UNUSED) -{ -} - - -/* - * shaper font data - */ - -struct hb_coretext_aat_shaper_font_data_t {}; - -hb_coretext_aat_shaper_font_data_t * -_hb_coretext_aat_shaper_font_data_create (hb_font_t *font) -{ - return hb_coretext_shaper_font_data_ensure (font) ? (hb_coretext_aat_shaper_font_data_t *) HB_SHAPER_DATA_SUCCEEDED : nullptr; -} - -void -_hb_coretext_aat_shaper_font_data_destroy (hb_coretext_aat_shaper_font_data_t *data HB_UNUSED) -{ -} - - -/* - * shaper shape_plan data - */ - -struct hb_coretext_aat_shaper_shape_plan_data_t {}; - -hb_coretext_aat_shaper_shape_plan_data_t * -_hb_coretext_aat_shaper_shape_plan_data_create (hb_shape_plan_t *shape_plan HB_UNUSED, - const hb_feature_t *user_features HB_UNUSED, - unsigned int num_user_features HB_UNUSED, - const int *coords HB_UNUSED, - unsigned int num_coords HB_UNUSED) -{ - return (hb_coretext_aat_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED; -} - -void -_hb_coretext_aat_shaper_shape_plan_data_destroy (hb_coretext_aat_shaper_shape_plan_data_t *data HB_UNUSED) -{ -} - - -/* - * shaper - */ - -hb_bool_t -_hb_coretext_aat_shape (hb_shape_plan_t *shape_plan, - hb_font_t *font, - hb_buffer_t *buffer, - const hb_feature_t *features, - unsigned int num_features) -{ - return _hb_coretext_shape (shape_plan, font, buffer, features, num_features); -} diff --git a/src/3rdparty/harfbuzz-ng/src/hb-coretext.h b/src/3rdparty/harfbuzz-ng/src/hb-coretext.h index 12f7d2515bd..e53dbaf2c7f 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-coretext.h +++ b/src/3rdparty/harfbuzz-ng/src/hb-coretext.h @@ -40,10 +40,42 @@ HB_BEGIN_DECLS +/** + * HB_CORETEXT_TAG_MORT: + * + * The #hb_tag_t tag for the `mort` (glyph metamorphosis) table, + * which holds AAT features. + * + * For more information, see + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6mort.html + * + **/ #define HB_CORETEXT_TAG_MORT HB_TAG('m','o','r','t') + +/** + * HB_CORETEXT_TAG_MORX: + * + * The #hb_tag_t tag for the `morx` (extended glyph metamorphosis) + * table, which holds AAT features. + * + * For more information, see + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6morx.html + * + **/ #define HB_CORETEXT_TAG_MORX HB_TAG('m','o','r','x') + +/** + * HB_CORETEXT_TAG_KERX: + * + * The #hb_tag_t tag for the `kerx` (extended kerning) table, which + * holds AAT kerning information. + * + * For more information, see + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6kerx.html + * + **/ #define HB_CORETEXT_TAG_KERX HB_TAG('k','e','r','x') -#define HB_CORETEXT_TAG_TRAK HB_TAG('t','r','a','k') + HB_EXTERN hb_face_t * hb_coretext_face_create (CGFontRef cg_font); diff --git a/src/3rdparty/harfbuzz-ng/src/hb-cplusplus.hh b/src/3rdparty/harfbuzz-ng/src/hb-cplusplus.hh new file mode 100644 index 00000000000..531ef1b7c83 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-cplusplus.hh @@ -0,0 +1,223 @@ +/* + * Copyright © 2022 Behdad Esfahbod + * + * 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. + */ + +#ifndef HB_CPLUSPLUS_HH +#define HB_CPLUSPLUS_HH + +#include "hb.h" + +HB_BEGIN_DECLS +HB_END_DECLS + +#ifdef __cplusplus + +#include +#include + +#if 0 +#if !(__cplusplus >= 201103L) +#error "HarfBuzz C++ helpers require C++11" +#endif +#endif + +namespace hb { + + +template +struct vtable; + +template +struct shared_ptr +{ + using element_type = T; + + using v = vtable; + + explicit shared_ptr (T *p = nullptr) : p (p) {} + shared_ptr (const shared_ptr &o) : p (v::reference (o.p)) {} + shared_ptr (shared_ptr &&o) : p (o.p) { o.p = nullptr; } + shared_ptr& operator = (const shared_ptr &o) { if (p != o.p) { destroy (); p = o.p; reference (); } return *this; } + shared_ptr& operator = (shared_ptr &&o) { v::destroy (p); p = o.p; o.p = nullptr; return *this; } + ~shared_ptr () { v::destroy (p); p = nullptr; } + + T* get() const { return p; } + + void swap (shared_ptr &o) { std::swap (p, o.p); } + friend void swap (shared_ptr &a, shared_ptr &b) { std::swap (a.p, b.p); } + + operator T * () const { return p; } + T& operator * () const { return *get (); } + T* operator -> () const { return get (); } + operator bool () const { return p; } + bool operator == (const shared_ptr &o) const { return p == o.p; } + bool operator != (const shared_ptr &o) const { return p != o.p; } + + static T* get_empty() { return v::get_empty (); } + T* reference() { return v::reference (p); } + void destroy() { v::destroy (p); } + void set_user_data (hb_user_data_key_t *key, + void *value, + hb_destroy_func_t destroy, + hb_bool_t replace) { v::set_user_data (p, key, value, destroy, replace); } + void * get_user_data (hb_user_data_key_t *key) { return v::get_user_data (p, key); } + + private: + T *p; +}; + +template struct is_shared_ptr : std::false_type {}; +template struct is_shared_ptr> : std::true_type {}; + +template +struct unique_ptr +{ + using element_type = T; + + using v = vtable; + + explicit unique_ptr (T *p = nullptr) : p (p) {} + unique_ptr (const unique_ptr &o) = delete; + unique_ptr (unique_ptr &&o) : p (o.p) { o.p = nullptr; } + unique_ptr& operator = (const unique_ptr &o) = delete; + unique_ptr& operator = (unique_ptr &&o) { v::destroy (p); p = o.p; o.p = nullptr; return *this; } + ~unique_ptr () { v::destroy (p); p = nullptr; } + + T* get() const { return p; } + T* release () { T* v = p; p = nullptr; return v; } + + void swap (unique_ptr &o) { std::swap (p, o.p); } + friend void swap (unique_ptr &a, unique_ptr &b) { std::swap (a.p, b.p); } + + operator T * () const { return p; } + T& operator * () const { return *get (); } + T* operator -> () const { return get (); } + operator bool () { return p; } + + private: + T *p; +}; + +template struct is_unique_ptr : std::false_type {}; +template struct is_unique_ptr> : std::true_type {}; + +template +struct vtable_t +{ + static constexpr auto get_empty = _get_empty; + static constexpr auto reference = _reference; + static constexpr auto destroy = _destroy; + static constexpr auto set_user_data = _set_user_data; + static constexpr auto get_user_data = _get_user_data; +}; + +#define HB_DEFINE_VTABLE(name) \ + template<> \ + struct vtable \ + : vtable_t {} + +HB_DEFINE_VTABLE (buffer); +HB_DEFINE_VTABLE (blob); +HB_DEFINE_VTABLE (face); +HB_DEFINE_VTABLE (font); +HB_DEFINE_VTABLE (font_funcs); +HB_DEFINE_VTABLE (map); +HB_DEFINE_VTABLE (set); +HB_DEFINE_VTABLE (shape_plan); +HB_DEFINE_VTABLE (unicode_funcs); +HB_DEFINE_VTABLE (draw_funcs); +HB_DEFINE_VTABLE (paint_funcs); + +#undef HB_DEFINE_VTABLE + + +#ifdef HB_SUBSET_H + +#define HB_DEFINE_VTABLE(name) \ + template<> \ + struct vtable \ + : vtable_t {} + + +HB_DEFINE_VTABLE (subset_input); +HB_DEFINE_VTABLE (subset_plan); + +#undef HB_DEFINE_VTABLE + +#endif + + +} // namespace hb + +/* Workaround for GCC < 7, see: + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56480 + * https://stackoverflow.com/a/25594741 */ +namespace std { + + +template +struct hash> +{ + std::size_t operator()(const hb::shared_ptr& v) const noexcept + { + std::size_t h = std::hash{}(v.get ()); + return h; + } +}; + +template +struct hash> +{ + std::size_t operator()(const hb::unique_ptr& v) const noexcept + { + std::size_t h = std::hash{}(v.get ()); + return h; + } +}; + + +} // namespace std + +#endif /* __cplusplus */ + +#endif /* HB_CPLUSPLUS_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-debug.hh b/src/3rdparty/harfbuzz-ng/src/hb-debug.hh index 6c425f7b9dd..559db4067e2 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-debug.hh +++ b/src/3rdparty/harfbuzz-ng/src/hb-debug.hh @@ -27,13 +27,62 @@ #ifndef HB_DEBUG_HH #define HB_DEBUG_HH -#include "hb-private.hh" +#include "hb.hh" +#include "hb-atomic.hh" +#include "hb-algs.hh" #ifndef HB_DEBUG #define HB_DEBUG 0 #endif + +/* + * Global runtime options. + */ + +struct hb_options_t +{ + bool unused : 1; /* In-case sign bit is here. */ + bool initialized : 1; + bool uniscribe_bug_compatible : 1; +}; + +union hb_options_union_t { + int i; + hb_options_t opts; +}; +static_assert ((sizeof (hb_atomic_int_t) >= sizeof (hb_options_union_t)), ""); + +HB_INTERNAL void +_hb_options_init (); + +extern HB_INTERNAL hb_atomic_int_t _hb_options; + +static inline hb_options_t +hb_options () +{ +#ifdef HB_NO_GETENV + return hb_options_t (); +#endif + /* Make a local copy, so we can access bitfield threadsafely. */ + hb_options_union_t u; + u.i = _hb_options; + + if (unlikely (!u.i)) + { + _hb_options_init (); + u.i = _hb_options; + } + + return u.opts; +} + + +/* + * Debug output (needs enabling at compile time.) + */ + static inline bool _hb_debug (unsigned int level, unsigned int max_level) @@ -64,7 +113,7 @@ _hb_print_func (const char *func) const char *paren = strchr (func, '('); if (paren) func_len = paren - func; - fprintf (stderr, "%.*s", func_len, func); + fprintf (stderr, "%.*s", (int) func_len, func); } } @@ -93,9 +142,9 @@ _hb_debug_msg_va (const char *what, fprintf (stderr, "%-10s", what ? what : ""); if (obj) - fprintf (stderr, "(%*p) ", (unsigned int) (2 * sizeof (void *)), obj); + fprintf (stderr, "(%*p) ", (int) (2 * sizeof (void *)), obj); else - fprintf (stderr, " %*s ", (unsigned int) (2 * sizeof (void *)), ""); + fprintf (stderr, " %*s ", (int) (2 * sizeof (void *)), ""); if (indented) { #define VBAR "\342\224\202" /* U+2502 BOX DRAWINGS LIGHT VERTICAL */ @@ -111,7 +160,7 @@ _hb_debug_msg_va (const char *what, VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR; fprintf (stderr, "%2u %s" VRBAR "%s", level, - bars + sizeof (bars) - 1 - MIN ((unsigned int) sizeof (bars) - 1, (unsigned int) (sizeof (VBAR) - 1) * level), + bars + sizeof (bars) - 1 - hb_min ((unsigned int) sizeof (bars) - 1, (unsigned int) (sizeof (VBAR) - 1) * level), level_dir ? (level_dir > 0 ? DLBAR : ULBAR) : LBAR); } else fprintf (stderr, " " VRBAR LBAR); @@ -126,7 +175,7 @@ _hb_debug_msg_va (const char *what, fprintf (stderr, "\n"); } -template <> inline void +template <> inline void HB_PRINTF_FUNC(7, 0) _hb_debug_msg_va<0> (const char *what HB_UNUSED, const void *obj HB_UNUSED, const char *func HB_UNUSED, @@ -145,7 +194,7 @@ _hb_debug_msg (const char *what, int level_dir, const char *message, ...) HB_PRINTF_FUNC(7, 8); -template static inline void +template static inline void HB_PRINTF_FUNC(7, 8) _hb_debug_msg (const char *what, const void *obj, const char *func, @@ -169,7 +218,7 @@ _hb_debug_msg<0> (const char *what HB_UNUSED, int level_dir HB_UNUSED, const char *message HB_UNUSED, ...) HB_PRINTF_FUNC(7, 8); -template <> inline void +template <> inline void HB_PRINTF_FUNC(7, 8) _hb_debug_msg<0> (const char *what HB_UNUSED, const void *obj HB_UNUSED, const char *func HB_UNUSED, @@ -180,7 +229,7 @@ _hb_debug_msg<0> (const char *what HB_UNUSED, ...) {} #define DEBUG_MSG_LEVEL(WHAT, OBJ, LEVEL, LEVEL_DIR, ...) _hb_debug_msg (#WHAT, (OBJ), nullptr, true, (LEVEL), (LEVEL_DIR), __VA_ARGS__) -#define DEBUG_MSG(WHAT, OBJ, ...) _hb_debug_msg (#WHAT, (OBJ), nullptr, false, 0, 0, __VA_ARGS__) +#define DEBUG_MSG(WHAT, OBJ, ...) _hb_debug_msg (#WHAT, (OBJ), nullptr, false, 0, 0, __VA_ARGS__) #define DEBUG_MSG_FUNC(WHAT, OBJ, ...) _hb_debug_msg (#WHAT, (OBJ), HB_FUNC, false, 0, 0, __VA_ARGS__) @@ -199,8 +248,8 @@ struct hb_printer_t { }; template <> -struct hb_printer_t { - const char *print (hb_void_t) { return ""; } +struct hb_printer_t { + const char *print (hb_empty_t) { return ""; } }; @@ -216,8 +265,9 @@ static inline void _hb_warn_no_return (bool returned) } } template <> -/*static*/ inline void _hb_warn_no_return (bool returned HB_UNUSED) -{} +/*static*/ inline void _hb_warn_no_return (bool returned HB_UNUSED) {} +template <> +/*static*/ inline void _hb_warn_no_return (bool returned HB_UNUSED) {} template struct hb_auto_trace_t @@ -237,7 +287,7 @@ struct hb_auto_trace_t _hb_debug_msg_va (what, obj, func, true, plevel ? *plevel : 0, +1, message, ap); va_end (ap); } - inline ~hb_auto_trace_t (void) + ~hb_auto_trace_t () { _hb_warn_no_return (returned); if (!returned) { @@ -246,20 +296,23 @@ struct hb_auto_trace_t if (plevel) --*plevel; } - inline ret_t ret (ret_t v, unsigned int line = 0) + template + T ret (T&& v, + const char *func = "", + unsigned int line = 0) { if (unlikely (returned)) { fprintf (stderr, "OUCH, double calls to return_trace(). This is a bug, please report.\n"); - return v; + return std::forward (v); } - _hb_debug_msg (what, obj, nullptr, true, plevel ? *plevel : 1, -1, - "return %s (line %d)", - hb_printer_t().print (v), line); + _hb_debug_msg (what, obj, func, true, plevel ? *plevel : 1, -1, + "return %s (line %u)", + hb_printer_t>().print (v), line); if (plevel) --*plevel; plevel = nullptr; returned = true; - return v; + return std::forward (v); } private: @@ -278,17 +331,23 @@ struct hb_auto_trace_t<0, ret_t> const char *message, ...) HB_PRINTF_FUNC(6, 7) {} - inline ret_t ret (ret_t v, unsigned int line HB_UNUSED = 0) { return v; } + template + T ret (T&& v, + const char *func HB_UNUSED = nullptr, + unsigned int line HB_UNUSED = 0) { return std::forward (v); } }; /* For disabled tracing; optimize out everything. * https://github.com/harfbuzz/harfbuzz/pull/605 */ template struct hb_no_trace_t { - inline ret_t ret (ret_t v, unsigned int line HB_UNUSED = 0) { return v; } + template + T ret (T&& v, + const char *func HB_UNUSED = nullptr, + unsigned int line HB_UNUSED = 0) { return std::forward (v); } }; -#define return_trace(RET) return trace.ret (RET, __LINE__) +#define return_trace(RET) return trace.ret (RET, HB_FUNC, __LINE__) /* @@ -315,8 +374,8 @@ struct hb_no_trace_t { #define HB_DEBUG_FT (HB_DEBUG+0) #endif -#ifndef HB_DEBUG_GET_COVERAGE -#define HB_DEBUG_GET_COVERAGE (HB_DEBUG+0) +#ifndef HB_DEBUG_JUSTIFY +#define HB_DEBUG_JUSTIFY (HB_DEBUG+0) #endif #ifndef HB_DEBUG_OBJECT @@ -331,6 +390,10 @@ struct hb_no_trace_t { #define HB_DEBUG_UNISCRIBE (HB_DEBUG+0) #endif +#ifndef HB_DEBUG_WASM +#define HB_DEBUG_WASM (HB_DEBUG+0) +#endif + /* * With tracing. */ @@ -342,36 +405,12 @@ struct hb_no_trace_t { #define TRACE_APPLY(this) \ hb_auto_trace_t trace \ (&c->debug_depth, c->get_name (), this, HB_FUNC, \ - "idx %d gid %u lookup %d", \ + "idx %u gid %u lookup %d", \ c->buffer->idx, c->buffer->cur().codepoint, (int) c->lookup_index) #else #define TRACE_APPLY(this) hb_no_trace_t trace #endif -#ifndef HB_DEBUG_CLOSURE -#define HB_DEBUG_CLOSURE (HB_DEBUG+0) -#endif -#if HB_DEBUG_CLOSURE -#define TRACE_CLOSURE(this) \ - hb_auto_trace_t trace \ - (&c->debug_depth, c->get_name (), this, HB_FUNC, \ - " ") -#else -#define TRACE_CLOSURE(this) hb_no_trace_t trace HB_UNUSED -#endif - -#ifndef HB_DEBUG_COLLECT_GLYPHS -#define HB_DEBUG_COLLECT_GLYPHS (HB_DEBUG+0) -#endif -#if HB_DEBUG_COLLECT_GLYPHS -#define TRACE_COLLECT_GLYPHS(this) \ - hb_auto_trace_t trace \ - (&c->debug_depth, c->get_name (), this, HB_FUNC, \ - " ") -#else -#define TRACE_COLLECT_GLYPHS(this) hb_no_trace_t trace HB_UNUSED -#endif - #ifndef HB_DEBUG_SANITIZE #define HB_DEBUG_SANITIZE (HB_DEBUG+0) #endif @@ -379,7 +418,7 @@ struct hb_no_trace_t { #define TRACE_SANITIZE(this) \ hb_auto_trace_t trace \ (&c->debug_depth, c->get_name (), this, HB_FUNC, \ - " "); + " ") #else #define TRACE_SANITIZE(this) hb_no_trace_t trace #endif @@ -391,41 +430,62 @@ struct hb_no_trace_t { #define TRACE_SERIALIZE(this) \ hb_auto_trace_t trace \ (&c->debug_depth, "SERIALIZE", c, HB_FUNC, \ - " "); + " ") #else #define TRACE_SERIALIZE(this) hb_no_trace_t trace #endif -#ifndef HB_DEBUG_WOULD_APPLY -#define HB_DEBUG_WOULD_APPLY (HB_DEBUG+0) +#ifndef HB_DEBUG_SUBSET +#define HB_DEBUG_SUBSET (HB_DEBUG+0) #endif -#if HB_DEBUG_WOULD_APPLY -#define TRACE_WOULD_APPLY(this) \ - hb_auto_trace_t trace \ - (&c->debug_depth, c->get_name (), this, HB_FUNC, \ - "%d glyphs", c->len); +#if HB_DEBUG_SUBSET +#define TRACE_SUBSET(this) \ + hb_auto_trace_t trace \ + (&c->debug_depth, c->get_name (), this, HB_FUNC, \ + " ") #else -#define TRACE_WOULD_APPLY(this) hb_no_trace_t trace +#define TRACE_SUBSET(this) hb_no_trace_t trace #endif +#ifndef HB_DEBUG_SUBSET_REPACK +#define HB_DEBUG_SUBSET_REPACK (HB_DEBUG+0) +#endif + +#ifndef HB_DEBUG_PAINT +#define HB_DEBUG_PAINT (HB_DEBUG+0) +#endif +#if HB_DEBUG_PAINT +#define TRACE_PAINT(this) \ + HB_UNUSED hb_auto_trace_t trace \ + (&c->debug_depth, c->get_name (), this, HB_FUNC, \ + " ") +#else +#define TRACE_PAINT(this) HB_UNUSED hb_no_trace_t trace +#endif + + #ifndef HB_DEBUG_DISPATCH #define HB_DEBUG_DISPATCH ( \ HB_DEBUG_APPLY + \ - HB_DEBUG_CLOSURE + \ - HB_DEBUG_COLLECT_GLYPHS + \ HB_DEBUG_SANITIZE + \ HB_DEBUG_SERIALIZE + \ - HB_DEBUG_WOULD_APPLY + \ + HB_DEBUG_SUBSET + \ + HB_DEBUG_PAINT + \ 0) #endif #if HB_DEBUG_DISPATCH #define TRACE_DISPATCH(this, format) \ hb_auto_trace_t trace \ (&c->debug_depth, c->get_name (), this, HB_FUNC, \ - "format %d", (int) format); + "format %u", (unsigned) format) #else #define TRACE_DISPATCH(this, format) hb_no_trace_t trace #endif +#ifndef HB_BUFFER_MESSAGE_MORE +#define HB_BUFFER_MESSAGE_MORE (HB_DEBUG+1) +#endif + + #endif /* HB_DEBUG_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-deprecated.h b/src/3rdparty/harfbuzz-ng/src/hb-deprecated.h index eac7efb42f1..9fcce6d9ac4 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-deprecated.h +++ b/src/3rdparty/harfbuzz-ng/src/hb-deprecated.h @@ -24,7 +24,7 @@ * Google Author(s): Behdad Esfahbod */ -#ifndef HB_H_IN +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) #error "Include instead." #endif @@ -36,30 +36,274 @@ #include "hb-font.h" #include "hb-set.h" + +/** + * SECTION:hb-deprecated + * @title: hb-deprecated + * @short_description: Deprecated API + * @include: hb.h + * + * These API have been deprecated in favor of newer API, or because they + * were deemed unnecessary. + **/ + + HB_BEGIN_DECLS #ifndef HB_DISABLE_DEPRECATED + +/** + * HB_SCRIPT_CANADIAN_ABORIGINAL: + * + * Use #HB_SCRIPT_CANADIAN_SYLLABICS instead: + * + * Deprecated: 0.9.20 + */ #define HB_SCRIPT_CANADIAN_ABORIGINAL HB_SCRIPT_CANADIAN_SYLLABICS +/** + * HB_BUFFER_FLAGS_DEFAULT: + * + * Use #HB_BUFFER_FLAG_DEFAULT instead. + * + * Deprecated: 0.9.20 + */ #define HB_BUFFER_FLAGS_DEFAULT HB_BUFFER_FLAG_DEFAULT +/** + * HB_BUFFER_SERIALIZE_FLAGS_DEFAULT: + * + * Use #HB_BUFFER_SERIALIZE_FLAG_DEFAULT instead. + * + * Deprecated: 0.9.20 + */ #define HB_BUFFER_SERIALIZE_FLAGS_DEFAULT HB_BUFFER_SERIALIZE_FLAG_DEFAULT +/** + * hb_font_get_glyph_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @unicode: The Unicode code point to query + * @variation_selector: The variation-selector code point to query + * @glyph: (out): The glyph ID retrieved + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the glyph ID for a specified Unicode code point + * font, with an optional variation selector. + * + * Return value: `true` if data found, `false` otherwise + * Deprecated: 1.2.3 + * + **/ typedef hb_bool_t (*hb_font_get_glyph_func_t) (hb_font_t *font, void *font_data, hb_codepoint_t unicode, hb_codepoint_t variation_selector, hb_codepoint_t *glyph, void *user_data); +HB_DEPRECATED_FOR (hb_font_funcs_set_nominal_glyph_func and hb_font_funcs_set_variation_glyph_func) HB_EXTERN void hb_font_funcs_set_glyph_func (hb_font_funcs_t *ffuncs, hb_font_get_glyph_func_t func, void *user_data, hb_destroy_func_t destroy); +/* https://github.com/harfbuzz/harfbuzz/pull/4207 */ +/** + * HB_UNICODE_COMBINING_CLASS_CCC133: + * + * [Tibetan] + * + * Deprecated: 7.2.0 + **/ +#define HB_UNICODE_COMBINING_CLASS_CCC133 133 + +/** + * hb_unicode_eastasian_width_func_t: + * @ufuncs: A Unicode-functions structure + * @unicode: The code point to query + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_unicode_funcs_t structure. + * + * Deprecated: 2.0.0 + */ +typedef unsigned int (*hb_unicode_eastasian_width_func_t) (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t unicode, + void *user_data); + +/** + * hb_unicode_funcs_set_eastasian_width_func: + * @ufuncs: a Unicode-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_unicode_eastasian_width_func_t. + * + * Since: 0.9.2 + * Deprecated: 2.0.0 + **/ +HB_EXTERN HB_DEPRECATED void +hb_unicode_funcs_set_eastasian_width_func (hb_unicode_funcs_t *ufuncs, + hb_unicode_eastasian_width_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_unicode_eastasian_width: + * @ufuncs: a Unicode-function structure + * @unicode: The code point to query + * + * Don't use. Not used by HarfBuzz. + * + * Since: 0.9.2 + * Deprecated: 2.0.0 + **/ +HB_EXTERN HB_DEPRECATED unsigned int +hb_unicode_eastasian_width (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t unicode); + + +/** + * hb_unicode_decompose_compatibility_func_t: + * @ufuncs: a Unicode function structure + * @u: codepoint to decompose + * @decomposed: address of codepoint array (of length #HB_UNICODE_MAX_DECOMPOSITION_LEN) to write decomposition into + * @user_data: user data pointer as passed to hb_unicode_funcs_set_decompose_compatibility_func() + * + * Fully decompose @u to its Unicode compatibility decomposition. The codepoints of the decomposition will be written to @decomposed. + * The complete length of the decomposition will be returned. + * + * If @u has no compatibility decomposition, zero should be returned. + * + * The Unicode standard guarantees that a buffer of length #HB_UNICODE_MAX_DECOMPOSITION_LEN codepoints will always be sufficient for any + * compatibility decomposition plus an terminating value of 0. Consequently, @decompose must be allocated by the caller to be at least this length. Implementations + * of this function type must ensure that they do not write past the provided array. + * + * Return value: number of codepoints in the full compatibility decomposition of @u, or 0 if no decomposition available. + * + * Deprecated: 2.0.0 + */ +typedef unsigned int (*hb_unicode_decompose_compatibility_func_t) (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t u, + hb_codepoint_t *decomposed, + void *user_data); + +/** + * HB_UNICODE_MAX_DECOMPOSITION_LEN: + * + * See Unicode 6.1 for details on the maximum decomposition length. + * + * Deprecated: 2.0.0 + */ +#define HB_UNICODE_MAX_DECOMPOSITION_LEN (18+1) /* codepoints */ + +/** + * hb_unicode_funcs_set_decompose_compatibility_func: + * @ufuncs: A Unicode-functions structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_unicode_decompose_compatibility_func_t. + * + * + * + * Since: 0.9.2 + * Deprecated: 2.0.0 + **/ +HB_EXTERN HB_DEPRECATED void +hb_unicode_funcs_set_decompose_compatibility_func (hb_unicode_funcs_t *ufuncs, + hb_unicode_decompose_compatibility_func_t func, + void *user_data, hb_destroy_func_t destroy); + +HB_EXTERN HB_DEPRECATED unsigned int +hb_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t u, + hb_codepoint_t *decomposed); + + +/** + * hb_font_get_glyph_v_kerning_func_t: + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the kerning-adjustment value for a glyph-pair in + * the specified font, for vertical text segments. + * + **/ +typedef hb_font_get_glyph_kerning_func_t hb_font_get_glyph_v_kerning_func_t; + +/** + * hb_font_funcs_set_glyph_v_kerning_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_font_get_glyph_v_kerning_func_t. + * + * Since: 0.9.2 + * Deprecated: 2.0.0 + **/ HB_EXTERN void -hb_set_invert (hb_set_t *set); +hb_font_funcs_set_glyph_v_kerning_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_v_kerning_func_t func, + void *user_data, hb_destroy_func_t destroy); + +HB_EXTERN hb_position_t +hb_font_get_glyph_v_kerning (hb_font_t *font, + hb_codepoint_t top_glyph, hb_codepoint_t bottom_glyph); + + +/** + * hb_font_get_glyph_shape_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @glyph: The glyph ID to query + * @draw_funcs: The draw functions to send the shape data to + * @draw_data: The data accompanying the draw functions + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * Since: 4.0.0 + * Deprecated: 7.0.0: Use #hb_font_draw_glyph_func_t instead + **/ +typedef void (*hb_font_get_glyph_shape_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, void *draw_data, + void *user_data); + +/** + * hb_font_funcs_set_glyph_shape_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_font_get_glyph_shape_func_t, + * which is the same as #hb_font_draw_glyph_func_t. + * + * Since: 4.0.0 + * Deprecated: 7.0.0: Use hb_font_funcs_set_draw_glyph_func() instead + **/ +HB_DEPRECATED_FOR (hb_font_funcs_set_draw_glyph_func) +HB_EXTERN void +hb_font_funcs_set_glyph_shape_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_shape_func_t func, + void *user_data, hb_destroy_func_t destroy); + +HB_DEPRECATED_FOR (hb_font_draw_glyph) +HB_EXTERN void +hb_font_get_glyph_shape (hb_font_t *font, + hb_codepoint_t glyph, + hb_draw_funcs_t *dfuncs, void *draw_data); + #endif + HB_END_DECLS #endif /* HB_DEPRECATED_H */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-directwrite.cc b/src/3rdparty/harfbuzz-ng/src/hb-directwrite.cc new file mode 100644 index 00000000000..42764a244b1 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-directwrite.cc @@ -0,0 +1,884 @@ +/* + * Copyright © 2015-2019 Ebrahim Byagowi + * + * 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. + */ + +#include "hb.hh" + +#ifdef HAVE_DIRECTWRITE + +#include "hb-shaper-impl.hh" + +#include + +#include "hb-directwrite.h" + +#include "hb-ms-feature-ranges.hh" + +/** + * SECTION:hb-directwrite + * @title: hb-directwrite + * @short_description: DirectWrite integration + * @include: hb-directwrite.h + * + * Functions for using HarfBuzz with DirectWrite fonts. + **/ + +/* Declare object creator for dynamic support of DWRITE */ +typedef HRESULT (WINAPI *t_DWriteCreateFactory)( + DWRITE_FACTORY_TYPE factoryType, + REFIID iid, + IUnknown **factory +); + + +/* + * DirectWrite font stream helpers + */ + +// This is a font loader which provides only one font (unlike its original design). +// For a better implementation which was also source of this +// and DWriteFontFileStream, have a look at to NativeFontResourceDWrite.cpp in Mozilla +class DWriteFontFileLoader : public IDWriteFontFileLoader +{ +private: + IDWriteFontFileStream *mFontFileStream; +public: + DWriteFontFileLoader (IDWriteFontFileStream *fontFileStream) + { mFontFileStream = fontFileStream; } + + // IUnknown interface + IFACEMETHOD (QueryInterface) (IID const& iid, OUT void** ppObject) + { return S_OK; } + IFACEMETHOD_ (ULONG, AddRef) () { return 1; } + IFACEMETHOD_ (ULONG, Release) () { return 1; } + + // IDWriteFontFileLoader methods + virtual HRESULT STDMETHODCALLTYPE + CreateStreamFromKey (void const* fontFileReferenceKey, + uint32_t fontFileReferenceKeySize, + OUT IDWriteFontFileStream** fontFileStream) + { + *fontFileStream = mFontFileStream; + return S_OK; + } + + virtual ~DWriteFontFileLoader() {} +}; + +class DWriteFontFileStream : public IDWriteFontFileStream +{ +private: + uint8_t *mData; + uint32_t mSize; +public: + DWriteFontFileStream (uint8_t *aData, uint32_t aSize) + { + mData = aData; + mSize = aSize; + } + + // IUnknown interface + IFACEMETHOD (QueryInterface) (IID const& iid, OUT void** ppObject) + { return S_OK; } + IFACEMETHOD_ (ULONG, AddRef) () { return 1; } + IFACEMETHOD_ (ULONG, Release) () { return 1; } + + // IDWriteFontFileStream methods + virtual HRESULT STDMETHODCALLTYPE + ReadFileFragment (void const** fragmentStart, + UINT64 fileOffset, + UINT64 fragmentSize, + OUT void** fragmentContext) + { + // We are required to do bounds checking. + if (fileOffset + fragmentSize > mSize) return E_FAIL; + + // truncate the 64 bit fileOffset to size_t sized index into mData + size_t index = static_cast (fileOffset); + + // We should be alive for the duration of this. + *fragmentStart = &mData[index]; + *fragmentContext = nullptr; + return S_OK; + } + + virtual void STDMETHODCALLTYPE + ReleaseFileFragment (void* fragmentContext) {} + + virtual HRESULT STDMETHODCALLTYPE + GetFileSize (OUT UINT64* fileSize) + { + *fileSize = mSize; + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE + GetLastWriteTime (OUT UINT64* lastWriteTime) { return E_NOTIMPL; } + + virtual ~DWriteFontFileStream() {} +}; + + +/* +* shaper face data +*/ + +struct hb_directwrite_face_data_t +{ + HMODULE dwrite_dll; + IDWriteFactory *dwriteFactory; + IDWriteFontFile *fontFile; + DWriteFontFileStream *fontFileStream; + DWriteFontFileLoader *fontFileLoader; + IDWriteFontFace *fontFace; + hb_blob_t *faceBlob; +}; + +hb_directwrite_face_data_t * +_hb_directwrite_shaper_face_data_create (hb_face_t *face) +{ + hb_directwrite_face_data_t *data = new hb_directwrite_face_data_t; + if (unlikely (!data)) + return nullptr; + +#define FAIL(...) \ + HB_STMT_START { \ + DEBUG_MSG (DIRECTWRITE, nullptr, __VA_ARGS__); \ + return nullptr; \ + } HB_STMT_END + + data->dwrite_dll = LoadLibrary (TEXT ("DWRITE")); + if (unlikely (!data->dwrite_dll)) + FAIL ("Cannot find DWrite.DLL"); + + t_DWriteCreateFactory p_DWriteCreateFactory; + +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-function-type" +#endif + + p_DWriteCreateFactory = (t_DWriteCreateFactory) + GetProcAddress (data->dwrite_dll, "DWriteCreateFactory"); + +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + + if (unlikely (!p_DWriteCreateFactory)) + FAIL ("Cannot find DWriteCreateFactory()."); + + HRESULT hr; + + // TODO: factory and fontFileLoader should be cached separately + IDWriteFactory* dwriteFactory; + hr = p_DWriteCreateFactory (DWRITE_FACTORY_TYPE_SHARED, __uuidof (IDWriteFactory), + (IUnknown**) &dwriteFactory); + + if (unlikely (hr != S_OK)) + FAIL ("Failed to run DWriteCreateFactory()."); + + hb_blob_t *blob = hb_face_reference_blob (face); + DWriteFontFileStream *fontFileStream; + fontFileStream = new DWriteFontFileStream ((uint8_t *) hb_blob_get_data (blob, nullptr), + hb_blob_get_length (blob)); + + DWriteFontFileLoader *fontFileLoader = new DWriteFontFileLoader (fontFileStream); + dwriteFactory->RegisterFontFileLoader (fontFileLoader); + + IDWriteFontFile *fontFile; + uint64_t fontFileKey = 0; + hr = dwriteFactory->CreateCustomFontFileReference (&fontFileKey, sizeof (fontFileKey), + fontFileLoader, &fontFile); + + if (FAILED (hr)) + FAIL ("Failed to load font file from data!"); + + BOOL isSupported; + DWRITE_FONT_FILE_TYPE fileType; + DWRITE_FONT_FACE_TYPE faceType; + uint32_t numberOfFaces; + hr = fontFile->Analyze (&isSupported, &fileType, &faceType, &numberOfFaces); + if (FAILED (hr) || !isSupported) + FAIL ("Font file is not supported."); + +#undef FAIL + + IDWriteFontFace *fontFace; + dwriteFactory->CreateFontFace (faceType, 1, &fontFile, 0, + DWRITE_FONT_SIMULATIONS_NONE, &fontFace); + + data->dwriteFactory = dwriteFactory; + data->fontFile = fontFile; + data->fontFileStream = fontFileStream; + data->fontFileLoader = fontFileLoader; + data->fontFace = fontFace; + data->faceBlob = blob; + + return data; +} + +void +_hb_directwrite_shaper_face_data_destroy (hb_directwrite_face_data_t *data) +{ + if (data->fontFace) + data->fontFace->Release (); + if (data->fontFile) + data->fontFile->Release (); + if (data->dwriteFactory) + { + if (data->fontFileLoader) + data->dwriteFactory->UnregisterFontFileLoader (data->fontFileLoader); + data->dwriteFactory->Release (); + } + delete data->fontFileLoader; + delete data->fontFileStream; + hb_blob_destroy (data->faceBlob); + if (data->dwrite_dll) + FreeLibrary (data->dwrite_dll); + delete data; +} + + +/* + * shaper font data + */ + +struct hb_directwrite_font_data_t {}; + +hb_directwrite_font_data_t * +_hb_directwrite_shaper_font_data_create (hb_font_t *font) +{ + return (hb_directwrite_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; +} + +void +_hb_directwrite_shaper_font_data_destroy (hb_directwrite_font_data_t *data) +{ +} + + +// Most of TextAnalysis is originally written by Bas Schouten for Mozilla project +// but now is relicensed to MIT for HarfBuzz use +class TextAnalysis : public IDWriteTextAnalysisSource, public IDWriteTextAnalysisSink +{ +public: + + IFACEMETHOD (QueryInterface) (IID const& iid, OUT void** ppObject) + { return S_OK; } + IFACEMETHOD_ (ULONG, AddRef) () { return 1; } + IFACEMETHOD_ (ULONG, Release) () { return 1; } + + // A single contiguous run of characters containing the same analysis + // results. + struct Run + { + uint32_t mTextStart; // starting text position of this run + uint32_t mTextLength; // number of contiguous code units covered + uint32_t mGlyphStart; // starting glyph in the glyphs array + uint32_t mGlyphCount; // number of glyphs associated with this run + // text + DWRITE_SCRIPT_ANALYSIS mScript; + uint8_t mBidiLevel; + bool mIsSideways; + + bool ContainsTextPosition (uint32_t aTextPosition) const + { + return aTextPosition >= mTextStart && + aTextPosition < mTextStart + mTextLength; + } + + Run *nextRun; + }; + +public: + TextAnalysis (const wchar_t* text, uint32_t textLength, + const wchar_t* localeName, DWRITE_READING_DIRECTION readingDirection) + : mTextLength (textLength), mText (text), mLocaleName (localeName), + mReadingDirection (readingDirection), mCurrentRun (nullptr) {} + ~TextAnalysis () + { + // delete runs, except mRunHead which is part of the TextAnalysis object + for (Run *run = mRunHead.nextRun; run;) + { + Run *origRun = run; + run = run->nextRun; + delete origRun; + } + } + + STDMETHODIMP + GenerateResults (IDWriteTextAnalyzer* textAnalyzer, Run **runHead) + { + // Analyzes the text using the script analyzer and returns + // the result as a series of runs. + + HRESULT hr = S_OK; + + // Initially start out with one result that covers the entire range. + // This result will be subdivided by the analysis processes. + mRunHead.mTextStart = 0; + mRunHead.mTextLength = mTextLength; + mRunHead.mBidiLevel = + (mReadingDirection == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT); + mRunHead.nextRun = nullptr; + mCurrentRun = &mRunHead; + + // Call each of the analyzers in sequence, recording their results. + if (SUCCEEDED (hr = textAnalyzer->AnalyzeScript (this, 0, mTextLength, this))) + *runHead = &mRunHead; + + return hr; + } + + // IDWriteTextAnalysisSource implementation + + IFACEMETHODIMP + GetTextAtPosition (uint32_t textPosition, + OUT wchar_t const** textString, + OUT uint32_t* textLength) + { + if (textPosition >= mTextLength) + { + // No text at this position, valid query though. + *textString = nullptr; + *textLength = 0; + } + else + { + *textString = mText + textPosition; + *textLength = mTextLength - textPosition; + } + return S_OK; + } + + IFACEMETHODIMP + GetTextBeforePosition (uint32_t textPosition, + OUT wchar_t const** textString, + OUT uint32_t* textLength) + { + if (textPosition == 0 || textPosition > mTextLength) + { + // Either there is no text before here (== 0), or this + // is an invalid position. The query is considered valid though. + *textString = nullptr; + *textLength = 0; + } + else + { + *textString = mText; + *textLength = textPosition; + } + return S_OK; + } + + IFACEMETHODIMP_ (DWRITE_READING_DIRECTION) + GetParagraphReadingDirection () { return mReadingDirection; } + + IFACEMETHODIMP GetLocaleName (uint32_t textPosition, uint32_t* textLength, + wchar_t const** localeName) + { return S_OK; } + + IFACEMETHODIMP + GetNumberSubstitution (uint32_t textPosition, + OUT uint32_t* textLength, + OUT IDWriteNumberSubstitution** numberSubstitution) + { + // We do not support number substitution. + *numberSubstitution = nullptr; + *textLength = mTextLength - textPosition; + + return S_OK; + } + + // IDWriteTextAnalysisSink implementation + + IFACEMETHODIMP + SetScriptAnalysis (uint32_t textPosition, uint32_t textLength, + DWRITE_SCRIPT_ANALYSIS const* scriptAnalysis) + { + SetCurrentRun (textPosition); + SplitCurrentRun (textPosition); + while (textLength > 0) + { + Run *run = FetchNextRun (&textLength); + run->mScript = *scriptAnalysis; + } + + return S_OK; + } + + IFACEMETHODIMP + SetLineBreakpoints (uint32_t textPosition, + uint32_t textLength, + const DWRITE_LINE_BREAKPOINT* lineBreakpoints) + { return S_OK; } + + IFACEMETHODIMP SetBidiLevel (uint32_t textPosition, uint32_t textLength, + uint8_t explicitLevel, uint8_t resolvedLevel) + { return S_OK; } + + IFACEMETHODIMP + SetNumberSubstitution (uint32_t textPosition, uint32_t textLength, + IDWriteNumberSubstitution* numberSubstitution) + { return S_OK; } + +protected: + Run *FetchNextRun (IN OUT uint32_t* textLength) + { + // Used by the sink setters, this returns a reference to the next run. + // Position and length are adjusted to now point after the current run + // being returned. + + Run *origRun = mCurrentRun; + // Split the tail if needed (the length remaining is less than the + // current run's size). + if (*textLength < mCurrentRun->mTextLength) + SplitCurrentRun (mCurrentRun->mTextStart + *textLength); + else + // Just advance the current run. + mCurrentRun = mCurrentRun->nextRun; + *textLength -= origRun->mTextLength; + + // Return a reference to the run that was just current. + return origRun; + } + + void SetCurrentRun (uint32_t textPosition) + { + // Move the current run to the given position. + // Since the analyzers generally return results in a forward manner, + // this will usually just return early. If not, find the + // corresponding run for the text position. + + if (mCurrentRun && mCurrentRun->ContainsTextPosition (textPosition)) + return; + + for (Run *run = &mRunHead; run; run = run->nextRun) + if (run->ContainsTextPosition (textPosition)) + { + mCurrentRun = run; + return; + } + assert (0); // We should always be able to find the text position in one of our runs + } + + void SplitCurrentRun (uint32_t splitPosition) + { + if (!mCurrentRun) + { + assert (0); // SplitCurrentRun called without current run + // Shouldn't be calling this when no current run is set! + return; + } + // Split the current run. + if (splitPosition <= mCurrentRun->mTextStart) + { + // No need to split, already the start of a run + // or before it. Usually the first. + return; + } + Run *newRun = new Run; + + *newRun = *mCurrentRun; + + // Insert the new run in our linked list. + newRun->nextRun = mCurrentRun->nextRun; + mCurrentRun->nextRun = newRun; + + // Adjust runs' text positions and lengths. + uint32_t splitPoint = splitPosition - mCurrentRun->mTextStart; + newRun->mTextStart += splitPoint; + newRun->mTextLength -= splitPoint; + mCurrentRun->mTextLength = splitPoint; + mCurrentRun = newRun; + } + +protected: + // Input + // (weak references are fine here, since this class is a transient + // stack-based helper that doesn't need to copy data) + uint32_t mTextLength; + const wchar_t* mText; + const wchar_t* mLocaleName; + DWRITE_READING_DIRECTION mReadingDirection; + + // Current processing state. + Run *mCurrentRun; + + // Output is a list of runs starting here + Run mRunHead; +}; + +/* + * shaper + */ + +hb_bool_t +_hb_directwrite_shape (hb_shape_plan_t *shape_plan, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features) +{ + hb_face_t *face = font->face; + const hb_directwrite_face_data_t *face_data = face->data.directwrite; + IDWriteFactory *dwriteFactory = face_data->dwriteFactory; + IDWriteFontFace *fontFace = face_data->fontFace; + + IDWriteTextAnalyzer* analyzer; + dwriteFactory->CreateTextAnalyzer (&analyzer); + + unsigned int scratch_size; + hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size); +#define ALLOCATE_ARRAY(Type, name, len) \ + Type *name = (Type *) scratch; \ + do { \ + unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \ + assert (_consumed <= scratch_size); \ + scratch += _consumed; \ + scratch_size -= _consumed; \ + } while (0) + +#define utf16_index() var1.u32 + + ALLOCATE_ARRAY (wchar_t, textString, buffer->len * 2); + + unsigned int chars_len = 0; + for (unsigned int i = 0; i < buffer->len; i++) + { + hb_codepoint_t c = buffer->info[i].codepoint; + buffer->info[i].utf16_index () = chars_len; + if (likely (c <= 0xFFFFu)) + textString[chars_len++] = c; + else if (unlikely (c > 0x10FFFFu)) + textString[chars_len++] = 0xFFFDu; + else + { + textString[chars_len++] = 0xD800u + ((c - 0x10000u) >> 10); + textString[chars_len++] = 0xDC00u + ((c - 0x10000u) & ((1u << 10) - 1)); + } + } + + ALLOCATE_ARRAY (WORD, log_clusters, chars_len); + /* Need log_clusters to assign features. */ + chars_len = 0; + for (unsigned int i = 0; i < buffer->len; i++) + { + hb_codepoint_t c = buffer->info[i].codepoint; + unsigned int cluster = buffer->info[i].cluster; + log_clusters[chars_len++] = cluster; + if (hb_in_range (c, 0x10000u, 0x10FFFFu)) + log_clusters[chars_len++] = cluster; /* Surrogates. */ + } + + DWRITE_READING_DIRECTION readingDirection; + readingDirection = buffer->props.direction ? + DWRITE_READING_DIRECTION_RIGHT_TO_LEFT : + DWRITE_READING_DIRECTION_LEFT_TO_RIGHT; + + /* + * There's an internal 16-bit limit on some things inside the analyzer, + * but we never attempt to shape a word longer than 64K characters + * in a single gfxShapedWord, so we cannot exceed that limit. + */ + uint32_t textLength = chars_len; + + TextAnalysis analysis (textString, textLength, nullptr, readingDirection); + TextAnalysis::Run *runHead; + HRESULT hr; + hr = analysis.GenerateResults (analyzer, &runHead); + +#define FAIL(...) \ + HB_STMT_START { \ + DEBUG_MSG (DIRECTWRITE, nullptr, __VA_ARGS__); \ + return false; \ + } HB_STMT_END + + if (FAILED (hr)) + FAIL ("Analyzer failed to generate results."); + + uint32_t maxGlyphCount = 3 * textLength / 2 + 16; + uint32_t glyphCount; + bool isRightToLeft = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); + + const wchar_t localeName[20] = {0}; + if (buffer->props.language) + mbstowcs ((wchar_t*) localeName, + hb_language_to_string (buffer->props.language), 20); + + /* + * Set up features. + */ + static_assert ((sizeof (DWRITE_TYPOGRAPHIC_FEATURES) == sizeof (hb_ms_features_t)), ""); + static_assert ((sizeof (DWRITE_FONT_FEATURE) == sizeof (hb_ms_feature_t)), ""); + hb_vector_t range_features; + hb_vector_t range_char_counts; + if (num_features) + { + hb_vector_t feature_records; + hb_vector_t range_records; + if (hb_ms_setup_features (features, num_features, feature_records, range_records)) + hb_ms_make_feature_ranges (feature_records, + range_records, + 0, + chars_len, + log_clusters, + range_features, + range_char_counts); + } + + uint16_t* clusterMap; + clusterMap = new uint16_t[textLength]; + DWRITE_SHAPING_TEXT_PROPERTIES* textProperties; + textProperties = new DWRITE_SHAPING_TEXT_PROPERTIES[textLength]; + +retry_getglyphs: + uint16_t* glyphIndices = new uint16_t[maxGlyphCount]; + DWRITE_SHAPING_GLYPH_PROPERTIES* glyphProperties; + glyphProperties = new DWRITE_SHAPING_GLYPH_PROPERTIES[maxGlyphCount]; + + hr = analyzer->GetGlyphs (textString, + chars_len, + fontFace, + false, + isRightToLeft, + &runHead->mScript, + localeName, + nullptr, + (const DWRITE_TYPOGRAPHIC_FEATURES**) range_features.arrayZ, + range_char_counts.arrayZ, + range_features.length, + maxGlyphCount, + clusterMap, + textProperties, + glyphIndices, + glyphProperties, + &glyphCount); + + if (unlikely (hr == HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER))) + { + delete [] glyphIndices; + delete [] glyphProperties; + + maxGlyphCount *= 2; + + goto retry_getglyphs; + } + if (FAILED (hr)) + FAIL ("Analyzer failed to get glyphs."); + + float* glyphAdvances = new float[maxGlyphCount]; + DWRITE_GLYPH_OFFSET* glyphOffsets = new DWRITE_GLYPH_OFFSET[maxGlyphCount]; + + /* The -2 in the following is to compensate for possible + * alignment needed after the WORD array. sizeof (WORD) == 2. */ + unsigned int glyphs_size = (scratch_size * sizeof (int) - 2) + / (sizeof (WORD) + + sizeof (DWRITE_SHAPING_GLYPH_PROPERTIES) + + sizeof (int) + + sizeof (DWRITE_GLYPH_OFFSET) + + sizeof (uint32_t)); + ALLOCATE_ARRAY (uint32_t, vis_clusters, glyphs_size); + +#undef ALLOCATE_ARRAY + + int fontEmSize = font->face->get_upem (); + if (fontEmSize < 0) fontEmSize = -fontEmSize; + + if (fontEmSize < 0) fontEmSize = -fontEmSize; + double x_mult = (double) font->x_scale / fontEmSize; + double y_mult = (double) font->y_scale / fontEmSize; + + hr = analyzer->GetGlyphPlacements (textString, + clusterMap, + textProperties, + chars_len, + glyphIndices, + glyphProperties, + glyphCount, + fontFace, + fontEmSize, + false, + isRightToLeft, + &runHead->mScript, + localeName, + (const DWRITE_TYPOGRAPHIC_FEATURES**) range_features.arrayZ, + range_char_counts.arrayZ, + range_features.length, + glyphAdvances, + glyphOffsets); + + if (FAILED (hr)) + FAIL ("Analyzer failed to get glyph placements."); + + /* Ok, we've got everything we need, now compose output buffer, + * very, *very*, carefully! */ + + /* Calculate visual-clusters. That's what we ship. */ + for (unsigned int i = 0; i < glyphCount; i++) + vis_clusters[i] = (uint32_t) -1; + for (unsigned int i = 0; i < buffer->len; i++) + { + uint32_t *p = + &vis_clusters[log_clusters[buffer->info[i].utf16_index ()]]; + *p = hb_min (*p, buffer->info[i].cluster); + } + for (unsigned int i = 1; i < glyphCount; i++) + if (vis_clusters[i] == (uint32_t) -1) + vis_clusters[i] = vis_clusters[i - 1]; + +#undef utf16_index + + if (unlikely (!buffer->ensure (glyphCount))) + FAIL ("Buffer in error"); + +#undef FAIL + + /* Set glyph infos */ + buffer->len = 0; + for (unsigned int i = 0; i < glyphCount; i++) + { + hb_glyph_info_t *info = &buffer->info[buffer->len++]; + + info->codepoint = glyphIndices[i]; + info->cluster = vis_clusters[i]; + + /* The rest is crap. Let's store position info there for now. */ + info->mask = glyphAdvances[i]; + info->var1.i32 = glyphOffsets[i].advanceOffset; + info->var2.i32 = glyphOffsets[i].ascenderOffset; + } + + /* Set glyph positions */ + buffer->clear_positions (); + for (unsigned int i = 0; i < glyphCount; i++) + { + hb_glyph_info_t *info = &buffer->info[i]; + hb_glyph_position_t *pos = &buffer->pos[i]; + + /* TODO vertical */ + pos->x_advance = x_mult * (int32_t) info->mask; + pos->x_offset = x_mult * (isRightToLeft ? -info->var1.i32 : info->var1.i32); + pos->y_offset = y_mult * info->var2.i32; + } + + if (isRightToLeft) hb_buffer_reverse (buffer); + + buffer->clear_glyph_flags (); + buffer->unsafe_to_break (); + + delete [] clusterMap; + delete [] glyphIndices; + delete [] textProperties; + delete [] glyphProperties; + delete [] glyphAdvances; + delete [] glyphOffsets; + + /* Wow, done! */ + return true; +} + +struct _hb_directwrite_font_table_context { + IDWriteFontFace *face; + void *table_context; +}; + +static void +_hb_directwrite_table_data_release (void *data) +{ + _hb_directwrite_font_table_context *context = (_hb_directwrite_font_table_context *) data; + context->face->ReleaseFontTable (context->table_context); + hb_free (context); +} + +static hb_blob_t * +_hb_directwrite_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) +{ + IDWriteFontFace *dw_face = ((IDWriteFontFace *) user_data); + const void *data; + uint32_t length; + void *table_context; + BOOL exists; + if (!dw_face || FAILED (dw_face->TryGetFontTable (hb_uint32_swap (tag), &data, + &length, &table_context, &exists))) + return nullptr; + + if (!data || !exists || !length) + { + dw_face->ReleaseFontTable (table_context); + return nullptr; + } + + _hb_directwrite_font_table_context *context = (_hb_directwrite_font_table_context *) hb_malloc (sizeof (_hb_directwrite_font_table_context)); + context->face = dw_face; + context->table_context = table_context; + + return hb_blob_create ((const char *) data, length, HB_MEMORY_MODE_READONLY, + context, _hb_directwrite_table_data_release); +} + +static void +_hb_directwrite_font_release (void *data) +{ + if (data) + ((IDWriteFontFace *) data)->Release (); +} + +/** + * hb_directwrite_face_create: + * @font_face: a DirectWrite IDWriteFontFace object. + * + * Constructs a new face object from the specified DirectWrite IDWriteFontFace. + * + * Return value: #hb_face_t object corresponding to the given input + * + * Since: 2.4.0 + **/ +hb_face_t * +hb_directwrite_face_create (IDWriteFontFace *font_face) +{ + if (font_face) + font_face->AddRef (); + return hb_face_create_for_tables (_hb_directwrite_reference_table, font_face, + _hb_directwrite_font_release); +} + +/** +* hb_directwrite_face_get_font_face: +* @face: a #hb_face_t object +* +* Gets the DirectWrite IDWriteFontFace associated with @face. +* +* Return value: DirectWrite IDWriteFontFace object corresponding to the given input +* +* Since: 2.5.0 +**/ +IDWriteFontFace * +hb_directwrite_face_get_font_face (hb_face_t *face) +{ + return face->data.directwrite->fontFace; +} + + +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-directwrite.h b/src/3rdparty/harfbuzz-ng/src/hb-directwrite.h new file mode 100644 index 00000000000..f837627a286 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-directwrite.h @@ -0,0 +1,40 @@ +/* + * Copyright © 2015-2019 Ebrahim Byagowi + * + * 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. + */ + +#ifndef HB_DIRECTWRITE_H +#define HB_DIRECTWRITE_H + +#include "hb.h" + +HB_BEGIN_DECLS + +HB_EXTERN hb_face_t * +hb_directwrite_face_create (IDWriteFontFace *font_face); + +HB_EXTERN IDWriteFontFace * +hb_directwrite_face_get_font_face (hb_face_t *face); + +HB_END_DECLS + +#endif /* HB_DIRECTWRITE_H */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-dispatch.hh b/src/3rdparty/harfbuzz-ng/src/hb-dispatch.hh new file mode 100644 index 00000000000..37ca6814653 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-dispatch.hh @@ -0,0 +1,60 @@ +/* + * Copyright © 2007,2008,2009,2010 Red Hat, Inc. + * Copyright © 2012,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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_DISPATCH_HH +#define HB_DISPATCH_HH + +#include "hb.hh" + +/* + * Dispatch + */ + +template +struct hb_dispatch_context_t +{ + private: + /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */ + const Context* thiz () const { return static_cast (this); } + Context* thiz () { return static_cast< Context *> (this); } + public: + const char *get_name () { return "UNKNOWN"; } + static constexpr unsigned max_debug_depth = MaxDebugDepth; + typedef Return return_t; + template + bool may_dispatch (const T *obj HB_UNUSED, const F *format HB_UNUSED) { return true; } + template + return_t dispatch (const T &obj, Ts&&... ds) + { return obj.dispatch (thiz (), std::forward (ds)...); } + static return_t no_dispatch_return_value () { return Context::default_return_value (); } + static bool stop_sublookup_iteration (const return_t r HB_UNUSED) { return false; } + unsigned debug_depth = 0; +}; + + +#endif /* HB_DISPATCH_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-draw.cc b/src/3rdparty/harfbuzz-ng/src/hb-draw.cc new file mode 100644 index 00000000000..f204f56bc7a --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-draw.cc @@ -0,0 +1,458 @@ +/* + * Copyright © 2019-2020 Ebrahim Byagowi + * + * 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. + */ + +#include "hb.hh" + +#ifndef HB_NO_DRAW + +#include "hb-draw.hh" + +/** + * SECTION:hb-draw + * @title: hb-draw + * @short_description: Glyph drawing + * @include: hb.h + * + * Functions for drawing (extracting) glyph shapes. + * + * The #hb_draw_funcs_t struct can be used with hb_font_draw_glyph(). + **/ + +static void +hb_draw_move_to_nil (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data HB_UNUSED, + hb_draw_state_t *st HB_UNUSED, + float to_x HB_UNUSED, float to_y HB_UNUSED, + void *user_data HB_UNUSED) {} + +static void +hb_draw_line_to_nil (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data HB_UNUSED, + hb_draw_state_t *st HB_UNUSED, + float to_x HB_UNUSED, float to_y HB_UNUSED, + void *user_data HB_UNUSED) {} + +static void +hb_draw_quadratic_to_nil (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + float control_x, float control_y, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ +#define HB_ONE_THIRD 0.33333333f + dfuncs->emit_cubic_to (draw_data, *st, + (st->current_x + 2.f * control_x) * HB_ONE_THIRD, + (st->current_y + 2.f * control_y) * HB_ONE_THIRD, + (to_x + 2.f * control_x) * HB_ONE_THIRD, + (to_y + 2.f * control_y) * HB_ONE_THIRD, + to_x, to_y); +#undef HB_ONE_THIRD +} + +static void +hb_draw_cubic_to_nil (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data HB_UNUSED, + hb_draw_state_t *st HB_UNUSED, + float control1_x HB_UNUSED, float control1_y HB_UNUSED, + float control2_x HB_UNUSED, float control2_y HB_UNUSED, + float to_x HB_UNUSED, float to_y HB_UNUSED, + void *user_data HB_UNUSED) {} + +static void +hb_draw_close_path_nil (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data HB_UNUSED, + hb_draw_state_t *st HB_UNUSED, + void *user_data HB_UNUSED) {} + + +static bool +_hb_draw_funcs_set_preamble (hb_draw_funcs_t *dfuncs, + bool func_is_null, + void **user_data, + hb_destroy_func_t *destroy) +{ + if (hb_object_is_immutable (dfuncs)) + { + if (*destroy) + (*destroy) (*user_data); + return false; + } + + if (func_is_null) + { + if (*destroy) + (*destroy) (*user_data); + *destroy = nullptr; + *user_data = nullptr; + } + + return true; +} + +static bool +_hb_draw_funcs_set_middle (hb_draw_funcs_t *dfuncs, + void *user_data, + hb_destroy_func_t destroy) +{ + if (user_data && !dfuncs->user_data) + { + dfuncs->user_data = (decltype (dfuncs->user_data)) hb_calloc (1, sizeof (*dfuncs->user_data)); + if (unlikely (!dfuncs->user_data)) + goto fail; + } + if (destroy && !dfuncs->destroy) + { + dfuncs->destroy = (decltype (dfuncs->destroy)) hb_calloc (1, sizeof (*dfuncs->destroy)); + if (unlikely (!dfuncs->destroy)) + goto fail; + } + + return true; + +fail: + if (destroy) + (destroy) (user_data); + return false; +} + +#define HB_DRAW_FUNC_IMPLEMENT(name) \ + \ +void \ +hb_draw_funcs_set_##name##_func (hb_draw_funcs_t *dfuncs, \ + hb_draw_##name##_func_t func, \ + void *user_data, \ + hb_destroy_func_t destroy) \ +{ \ + if (!_hb_draw_funcs_set_preamble (dfuncs, !func, &user_data, &destroy))\ + return; \ + \ + if (dfuncs->destroy && dfuncs->destroy->name) \ + dfuncs->destroy->name (!dfuncs->user_data ? nullptr : dfuncs->user_data->name); \ + \ + if (!_hb_draw_funcs_set_middle (dfuncs, user_data, destroy)) \ + return; \ + \ + if (func) \ + dfuncs->func.name = func; \ + else \ + dfuncs->func.name = hb_draw_##name##_nil; \ + \ + if (dfuncs->user_data) \ + dfuncs->user_data->name = user_data; \ + if (dfuncs->destroy) \ + dfuncs->destroy->name = destroy; \ +} + +HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_DRAW_FUNC_IMPLEMENT + +/** + * hb_draw_funcs_create: + * + * Creates a new draw callbacks object. + * + * Return value: (transfer full): + * A newly allocated #hb_draw_funcs_t with a reference count of 1. The initial + * reference count should be released with hb_draw_funcs_destroy when you are + * done using the #hb_draw_funcs_t. This function never returns `NULL`. If + * memory cannot be allocated, a special singleton #hb_draw_funcs_t object will + * be returned. + * + * Since: 4.0.0 + **/ +hb_draw_funcs_t * +hb_draw_funcs_create () +{ + hb_draw_funcs_t *dfuncs; + if (unlikely (!(dfuncs = hb_object_create ()))) + return const_cast (&Null (hb_draw_funcs_t)); + + dfuncs->func = Null (hb_draw_funcs_t).func; + + return dfuncs; +} + +DEFINE_NULL_INSTANCE (hb_draw_funcs_t) = +{ + HB_OBJECT_HEADER_STATIC, + + { +#define HB_DRAW_FUNC_IMPLEMENT(name) hb_draw_##name##_nil, + HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_DRAW_FUNC_IMPLEMENT + } +}; + +/** + * hb_draw_funcs_get_empty: + * + * Fetches the singleton empty draw-functions structure. + * + * Return value: (transfer full): The empty draw-functions structure + * + * Since: 7.0.0 + **/ +hb_draw_funcs_t * +hb_draw_funcs_get_empty () +{ + return const_cast (&Null (hb_draw_funcs_t)); +} + +/** + * hb_draw_funcs_reference: (skip) + * @dfuncs: draw functions + * + * Increases the reference count on @dfuncs by one. + * + * This prevents @dfuncs from being destroyed until a matching + * call to hb_draw_funcs_destroy() is made. + * + * Return value: (transfer full): + * The referenced #hb_draw_funcs_t. + * + * Since: 4.0.0 + **/ +hb_draw_funcs_t * +hb_draw_funcs_reference (hb_draw_funcs_t *dfuncs) +{ + return hb_object_reference (dfuncs); +} + +/** + * hb_draw_funcs_destroy: (skip) + * @dfuncs: draw functions + * + * Deallocate the @dfuncs. + * Decreases the reference count on @dfuncs by one. If the result is zero, then + * @dfuncs and all associated resources are freed. See hb_draw_funcs_reference(). + * + * Since: 4.0.0 + **/ +void +hb_draw_funcs_destroy (hb_draw_funcs_t *dfuncs) +{ + if (!hb_object_destroy (dfuncs)) return; + + if (dfuncs->destroy) + { +#define HB_DRAW_FUNC_IMPLEMENT(name) \ + if (dfuncs->destroy->name) dfuncs->destroy->name (!dfuncs->user_data ? nullptr : dfuncs->user_data->name); + HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_DRAW_FUNC_IMPLEMENT + } + + hb_free (dfuncs->destroy); + hb_free (dfuncs->user_data); + + hb_free (dfuncs); +} + +/** + * hb_draw_funcs_set_user_data: (skip) + * @dfuncs: The draw-functions structure + * @key: The user-data key + * @data: A pointer to the user data + * @destroy: (nullable): A callback to call when @data is not needed anymore + * @replace: Whether to replace an existing data with the same key + * + * Attaches a user-data key/data pair to the specified draw-functions structure. + * + * Return value: `true` if success, `false` otherwise + * + * Since: 7.0.0 + **/ +hb_bool_t +hb_draw_funcs_set_user_data (hb_draw_funcs_t *dfuncs, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (dfuncs, key, data, destroy, replace); +} + +/** + * hb_draw_funcs_get_user_data: (skip) + * @dfuncs: The draw-functions structure + * @key: The user-data key to query + * + * Fetches the user-data associated with the specified key, + * attached to the specified draw-functions structure. + * + * Return value: (transfer none): A pointer to the user data + * + * Since: 7.0.0 + **/ +void * +hb_draw_funcs_get_user_data (const hb_draw_funcs_t *dfuncs, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (dfuncs, key); +} + +/** + * hb_draw_funcs_make_immutable: + * @dfuncs: draw functions + * + * Makes @dfuncs object immutable. + * + * Since: 4.0.0 + **/ +void +hb_draw_funcs_make_immutable (hb_draw_funcs_t *dfuncs) +{ + if (hb_object_is_immutable (dfuncs)) + return; + + hb_object_make_immutable (dfuncs); +} + +/** + * hb_draw_funcs_is_immutable: + * @dfuncs: draw functions + * + * Checks whether @dfuncs is immutable. + * + * Return value: `true` if @dfuncs is immutable, `false` otherwise + * + * Since: 4.0.0 + **/ +hb_bool_t +hb_draw_funcs_is_immutable (hb_draw_funcs_t *dfuncs) +{ + return hb_object_is_immutable (dfuncs); +} + + +/** + * hb_draw_move_to: + * @dfuncs: draw functions + * @draw_data: associated draw data passed by the caller + * @st: current draw state + * @to_x: X component of target point + * @to_y: Y component of target point + * + * Perform a "move-to" draw operation. + * + * Since: 4.0.0 + **/ +void +hb_draw_move_to (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + float to_x, float to_y) +{ + dfuncs->move_to (draw_data, *st, + to_x, to_y); +} + +/** + * hb_draw_line_to: + * @dfuncs: draw functions + * @draw_data: associated draw data passed by the caller + * @st: current draw state + * @to_x: X component of target point + * @to_y: Y component of target point + * + * Perform a "line-to" draw operation. + * + * Since: 4.0.0 + **/ +void +hb_draw_line_to (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + float to_x, float to_y) +{ + dfuncs->line_to (draw_data, *st, + to_x, to_y); +} + +/** + * hb_draw_quadratic_to: + * @dfuncs: draw functions + * @draw_data: associated draw data passed by the caller + * @st: current draw state + * @control_x: X component of control point + * @control_y: Y component of control point + * @to_x: X component of target point + * @to_y: Y component of target point + * + * Perform a "quadratic-to" draw operation. + * + * Since: 4.0.0 + **/ +void +hb_draw_quadratic_to (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + float control_x, float control_y, + float to_x, float to_y) +{ + dfuncs->quadratic_to (draw_data, *st, + control_x, control_y, + to_x, to_y); +} + +/** + * hb_draw_cubic_to: + * @dfuncs: draw functions + * @draw_data: associated draw data passed by the caller + * @st: current draw state + * @control1_x: X component of first control point + * @control1_y: Y component of first control point + * @control2_x: X component of second control point + * @control2_y: Y component of second control point + * @to_x: X component of target point + * @to_y: Y component of target point + * + * Perform a "cubic-to" draw operation. + * + * Since: 4.0.0 + **/ +void +hb_draw_cubic_to (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + float control1_x, float control1_y, + float control2_x, float control2_y, + float to_x, float to_y) +{ + dfuncs->cubic_to (draw_data, *st, + control1_x, control1_y, + control2_x, control2_y, + to_x, to_y); +} + +/** + * hb_draw_close_path: + * @dfuncs: draw functions + * @draw_data: associated draw data passed by the caller + * @st: current draw state + * + * Perform a "close-path" draw operation. + * + * Since: 4.0.0 + **/ +void +hb_draw_close_path (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st) +{ + dfuncs->close_path (draw_data, *st); +} + + +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-draw.h b/src/3rdparty/harfbuzz-ng/src/hb-draw.h new file mode 100644 index 00000000000..9ca0b4006e4 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-draw.h @@ -0,0 +1,340 @@ +/* + * Copyright © 2019-2020 Ebrahim Byagowi + * + * 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. + */ + +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_DRAW_H +#define HB_DRAW_H + +#include "hb.h" + +HB_BEGIN_DECLS + + +/** + * hb_draw_state_t + * @path_open: Whether there is an open path + * @path_start_x: X component of the start of current path + * @path_start_y: Y component of the start of current path + * @current_x: X component of current point + * @current_y: Y component of current point + * + * Current drawing state. + * + * Since: 4.0.0 + **/ +typedef struct hb_draw_state_t { + hb_bool_t path_open; + + float path_start_x; + float path_start_y; + + float current_x; + float current_y; + + /*< private >*/ + hb_var_num_t reserved1; + hb_var_num_t reserved2; + hb_var_num_t reserved3; + hb_var_num_t reserved4; + hb_var_num_t reserved5; + hb_var_num_t reserved6; + hb_var_num_t reserved7; +} hb_draw_state_t; + +/** + * HB_DRAW_STATE_DEFAULT: + * + * The default #hb_draw_state_t at the start of glyph drawing. + */ +#define HB_DRAW_STATE_DEFAULT {0, 0.f, 0.f, 0.f, 0.f, {0.}, {0.}, {0.}} + + +/** + * hb_draw_funcs_t: + * + * Glyph draw callbacks. + * + * #hb_draw_move_to_func_t, #hb_draw_line_to_func_t and + * #hb_draw_cubic_to_func_t calls are necessary to be defined but we translate + * #hb_draw_quadratic_to_func_t calls to #hb_draw_cubic_to_func_t if the + * callback isn't defined. + * + * Since: 4.0.0 + **/ + +typedef struct hb_draw_funcs_t hb_draw_funcs_t; + + +/** + * hb_draw_move_to_func_t: + * @dfuncs: draw functions object + * @draw_data: The data accompanying the draw functions in hb_font_draw_glyph() + * @st: current draw state + * @to_x: X component of target point + * @to_y: Y component of target point + * @user_data: User data pointer passed to hb_draw_funcs_set_move_to_func() + * + * A virtual method for the #hb_draw_funcs_t to perform a "move-to" draw + * operation. + * + * Since: 4.0.0 + * + **/ +typedef void (*hb_draw_move_to_func_t) (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + float to_x, float to_y, + void *user_data); + +/** + * hb_draw_line_to_func_t: + * @dfuncs: draw functions object + * @draw_data: The data accompanying the draw functions in hb_font_draw_glyph() + * @st: current draw state + * @to_x: X component of target point + * @to_y: Y component of target point + * @user_data: User data pointer passed to hb_draw_funcs_set_line_to_func() + * + * A virtual method for the #hb_draw_funcs_t to perform a "line-to" draw + * operation. + * + * Since: 4.0.0 + * + **/ +typedef void (*hb_draw_line_to_func_t) (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + float to_x, float to_y, + void *user_data); + +/** + * hb_draw_quadratic_to_func_t: + * @dfuncs: draw functions object + * @draw_data: The data accompanying the draw functions in hb_font_draw_glyph() + * @st: current draw state + * @control_x: X component of control point + * @control_y: Y component of control point + * @to_x: X component of target point + * @to_y: Y component of target point + * @user_data: User data pointer passed to hb_draw_funcs_set_quadratic_to_func() + * + * A virtual method for the #hb_draw_funcs_t to perform a "quadratic-to" draw + * operation. + * + * Since: 4.0.0 + * + **/ +typedef void (*hb_draw_quadratic_to_func_t) (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + float control_x, float control_y, + float to_x, float to_y, + void *user_data); + +/** + * hb_draw_cubic_to_func_t: + * @dfuncs: draw functions object + * @draw_data: The data accompanying the draw functions in hb_font_draw_glyph() + * @st: current draw state + * @control1_x: X component of first control point + * @control1_y: Y component of first control point + * @control2_x: X component of second control point + * @control2_y: Y component of second control point + * @to_x: X component of target point + * @to_y: Y component of target point + * @user_data: User data pointer passed to hb_draw_funcs_set_cubic_to_func() + * + * A virtual method for the #hb_draw_funcs_t to perform a "cubic-to" draw + * operation. + * + * Since: 4.0.0 + * + **/ +typedef void (*hb_draw_cubic_to_func_t) (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + float control1_x, float control1_y, + float control2_x, float control2_y, + float to_x, float to_y, + void *user_data); + +/** + * hb_draw_close_path_func_t: + * @dfuncs: draw functions object + * @draw_data: The data accompanying the draw functions in hb_font_draw_glyph() + * @st: current draw state + * @user_data: User data pointer passed to hb_draw_funcs_set_close_path_func() + * + * A virtual method for the #hb_draw_funcs_t to perform a "close-path" draw + * operation. + * + * Since: 4.0.0 + * + **/ +typedef void (*hb_draw_close_path_func_t) (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + void *user_data); + +/** + * hb_draw_funcs_set_move_to_func: + * @dfuncs: draw functions object + * @func: (closure user_data) (destroy destroy) (scope notified): move-to callback + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets move-to callback to the draw functions object. + * + * Since: 4.0.0 + **/ +HB_EXTERN void +hb_draw_funcs_set_move_to_func (hb_draw_funcs_t *dfuncs, + hb_draw_move_to_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_draw_funcs_set_line_to_func: + * @dfuncs: draw functions object + * @func: (closure user_data) (destroy destroy) (scope notified): line-to callback + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets line-to callback to the draw functions object. + * + * Since: 4.0.0 + **/ +HB_EXTERN void +hb_draw_funcs_set_line_to_func (hb_draw_funcs_t *dfuncs, + hb_draw_line_to_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_draw_funcs_set_quadratic_to_func: + * @dfuncs: draw functions object + * @func: (closure user_data) (destroy destroy) (scope notified): quadratic-to callback + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets quadratic-to callback to the draw functions object. + * + * Since: 4.0.0 + **/ +HB_EXTERN void +hb_draw_funcs_set_quadratic_to_func (hb_draw_funcs_t *dfuncs, + hb_draw_quadratic_to_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_draw_funcs_set_cubic_to_func: + * @dfuncs: draw functions + * @func: (closure user_data) (destroy destroy) (scope notified): cubic-to callback + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets cubic-to callback to the draw functions object. + * + * Since: 4.0.0 + **/ +HB_EXTERN void +hb_draw_funcs_set_cubic_to_func (hb_draw_funcs_t *dfuncs, + hb_draw_cubic_to_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_draw_funcs_set_close_path_func: + * @dfuncs: draw functions object + * @func: (closure user_data) (destroy destroy) (scope notified): close-path callback + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets close-path callback to the draw functions object. + * + * Since: 4.0.0 + **/ +HB_EXTERN void +hb_draw_funcs_set_close_path_func (hb_draw_funcs_t *dfuncs, + hb_draw_close_path_func_t func, + void *user_data, hb_destroy_func_t destroy); + + +HB_EXTERN hb_draw_funcs_t * +hb_draw_funcs_create (void); + +HB_EXTERN hb_draw_funcs_t * +hb_draw_funcs_get_empty (void); + +HB_EXTERN hb_draw_funcs_t * +hb_draw_funcs_reference (hb_draw_funcs_t *dfuncs); + +HB_EXTERN void +hb_draw_funcs_destroy (hb_draw_funcs_t *dfuncs); + +HB_EXTERN hb_bool_t +hb_draw_funcs_set_user_data (hb_draw_funcs_t *dfuncs, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + + +HB_EXTERN void * +hb_draw_funcs_get_user_data (const hb_draw_funcs_t *dfuncs, + hb_user_data_key_t *key); + +HB_EXTERN void +hb_draw_funcs_make_immutable (hb_draw_funcs_t *dfuncs); + +HB_EXTERN hb_bool_t +hb_draw_funcs_is_immutable (hb_draw_funcs_t *dfuncs); + + +HB_EXTERN void +hb_draw_move_to (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + float to_x, float to_y); + +HB_EXTERN void +hb_draw_line_to (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + float to_x, float to_y); + +HB_EXTERN void +hb_draw_quadratic_to (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + float control_x, float control_y, + float to_x, float to_y); + +HB_EXTERN void +hb_draw_cubic_to (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + float control1_x, float control1_y, + float control2_x, float control2_y, + float to_x, float to_y); + +HB_EXTERN void +hb_draw_close_path (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st); + + +HB_END_DECLS + +#endif /* HB_DRAW_H */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-draw.hh b/src/3rdparty/harfbuzz-ng/src/hb-draw.hh new file mode 100644 index 00000000000..25dee1261ef --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-draw.hh @@ -0,0 +1,243 @@ +/* + * Copyright © 2020 Ebrahim Byagowi + * + * 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. + */ + +#ifndef HB_DRAW_HH +#define HB_DRAW_HH + +#include "hb.hh" + + +/* + * hb_draw_funcs_t + */ + +#define HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS \ + HB_DRAW_FUNC_IMPLEMENT (move_to) \ + HB_DRAW_FUNC_IMPLEMENT (line_to) \ + HB_DRAW_FUNC_IMPLEMENT (quadratic_to) \ + HB_DRAW_FUNC_IMPLEMENT (cubic_to) \ + HB_DRAW_FUNC_IMPLEMENT (close_path) \ + /* ^--- Add new callbacks here */ + +struct hb_draw_funcs_t +{ + hb_object_header_t header; + + struct { +#define HB_DRAW_FUNC_IMPLEMENT(name) hb_draw_##name##_func_t name; + HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_DRAW_FUNC_IMPLEMENT + } func; + + struct { +#define HB_DRAW_FUNC_IMPLEMENT(name) void *name; + HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_DRAW_FUNC_IMPLEMENT + } *user_data; + + struct { +#define HB_DRAW_FUNC_IMPLEMENT(name) hb_destroy_func_t name; + HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_DRAW_FUNC_IMPLEMENT + } *destroy; + + void emit_move_to (void *draw_data, hb_draw_state_t &st, + float to_x, float to_y) + { func.move_to (this, draw_data, &st, + to_x, to_y, + !user_data ? nullptr : user_data->move_to); } + void emit_line_to (void *draw_data, hb_draw_state_t &st, + float to_x, float to_y) + { func.line_to (this, draw_data, &st, + to_x, to_y, + !user_data ? nullptr : user_data->line_to); } + void emit_quadratic_to (void *draw_data, hb_draw_state_t &st, + float control_x, float control_y, + float to_x, float to_y) + { func.quadratic_to (this, draw_data, &st, + control_x, control_y, + to_x, to_y, + !user_data ? nullptr : user_data->quadratic_to); } + void emit_cubic_to (void *draw_data, hb_draw_state_t &st, + float control1_x, float control1_y, + float control2_x, float control2_y, + float to_x, float to_y) + { func.cubic_to (this, draw_data, &st, + control1_x, control1_y, + control2_x, control2_y, + to_x, to_y, + !user_data ? nullptr : user_data->cubic_to); } + void emit_close_path (void *draw_data, hb_draw_state_t &st) + { func.close_path (this, draw_data, &st, + !user_data ? nullptr : user_data->close_path); } + + + void + HB_ALWAYS_INLINE + move_to (void *draw_data, hb_draw_state_t &st, + float to_x, float to_y) + { + if (unlikely (st.path_open)) close_path (draw_data, st); + st.current_x = to_x; + st.current_y = to_y; + } + + void + HB_ALWAYS_INLINE + line_to (void *draw_data, hb_draw_state_t &st, + float to_x, float to_y) + { + if (unlikely (!st.path_open)) start_path (draw_data, st); + emit_line_to (draw_data, st, to_x, to_y); + st.current_x = to_x; + st.current_y = to_y; + } + + void + HB_ALWAYS_INLINE + quadratic_to (void *draw_data, hb_draw_state_t &st, + float control_x, float control_y, + float to_x, float to_y) + { + if (unlikely (!st.path_open)) start_path (draw_data, st); + emit_quadratic_to (draw_data, st, control_x, control_y, to_x, to_y); + st.current_x = to_x; + st.current_y = to_y; + } + + void + HB_ALWAYS_INLINE + cubic_to (void *draw_data, hb_draw_state_t &st, + float control1_x, float control1_y, + float control2_x, float control2_y, + float to_x, float to_y) + { + if (unlikely (!st.path_open)) start_path (draw_data, st); + emit_cubic_to (draw_data, st, control1_x, control1_y, control2_x, control2_y, to_x, to_y); + st.current_x = to_x; + st.current_y = to_y; + } + + void + HB_ALWAYS_INLINE + close_path (void *draw_data, hb_draw_state_t &st) + { + if (likely (st.path_open)) + { + if ((st.path_start_x != st.current_x) || (st.path_start_y != st.current_y)) + emit_line_to (draw_data, st, st.path_start_x, st.path_start_y); + emit_close_path (draw_data, st); + } + st.path_open = false; + st.path_start_x = st.current_x = st.path_start_y = st.current_y = 0; + } + + protected: + + void start_path (void *draw_data, hb_draw_state_t &st) + { + assert (!st.path_open); + emit_move_to (draw_data, st, st.current_x, st.current_y); + st.path_open = true; + st.path_start_x = st.current_x; + st.path_start_y = st.current_y; + } +}; +DECLARE_NULL_INSTANCE (hb_draw_funcs_t); + +struct hb_draw_session_t +{ + hb_draw_session_t (hb_draw_funcs_t *funcs_, void *draw_data_, float slant_ = 0.f) + : slant {slant_}, not_slanted {slant == 0.f}, + funcs {funcs_}, draw_data {draw_data_}, st HB_DRAW_STATE_DEFAULT + {} + + ~hb_draw_session_t () { close_path (); } + + HB_ALWAYS_INLINE + void move_to (float to_x, float to_y) + { + if (likely (not_slanted)) + funcs->move_to (draw_data, st, + to_x, to_y); + else + funcs->move_to (draw_data, st, + to_x + to_y * slant, to_y); + } + HB_ALWAYS_INLINE + void line_to (float to_x, float to_y) + { + if (likely (not_slanted)) + funcs->line_to (draw_data, st, + to_x, to_y); + else + funcs->line_to (draw_data, st, + to_x + to_y * slant, to_y); + } + void + HB_ALWAYS_INLINE + quadratic_to (float control_x, float control_y, + float to_x, float to_y) + { + if (likely (not_slanted)) + funcs->quadratic_to (draw_data, st, + control_x, control_y, + to_x, to_y); + else + funcs->quadratic_to (draw_data, st, + control_x + control_y * slant, control_y, + to_x + to_y * slant, to_y); + } + void + HB_ALWAYS_INLINE + cubic_to (float control1_x, float control1_y, + float control2_x, float control2_y, + float to_x, float to_y) + { + if (likely (not_slanted)) + funcs->cubic_to (draw_data, st, + control1_x, control1_y, + control2_x, control2_y, + to_x, to_y); + else + funcs->cubic_to (draw_data, st, + control1_x + control1_y * slant, control1_y, + control2_x + control2_y * slant, control2_y, + to_x + to_y * slant, to_y); + } + HB_ALWAYS_INLINE + void close_path () + { + funcs->close_path (draw_data, st); + } + + protected: + float slant; + bool not_slanted; + hb_draw_funcs_t *funcs; + void *draw_data; + hb_draw_state_t st; +}; + +#endif /* HB_DRAW_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-dsalgs.hh b/src/3rdparty/harfbuzz-ng/src/hb-dsalgs.hh deleted file mode 100644 index 4e8f0431c6a..00000000000 --- a/src/3rdparty/harfbuzz-ng/src/hb-dsalgs.hh +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright © 2017 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): Behdad Esfahbod - */ - -#ifndef HB_DSALGS_HH -#define HB_DSALGS_HH - -#include "hb-private.hh" - -#if defined(__ghs) -// GHS compiler doesn't support the __restrict keyword -#define __restrict -#endif - - - -static inline void * -hb_bsearch_r (const void *key, const void *base, - size_t nmemb, size_t size, - int (*compar)(const void *_key, const void *_item, void *_arg), - void *arg) -{ - int min = 0, max = (int) nmemb - 1; - while (min <= max) - { - int mid = (min + max) / 2; - const void *p = (const void *) (((const char *) base) + (mid * size)); - int c = compar (key, p, arg); - if (c < 0) - max = mid - 1; - else if (c > 0) - min = mid + 1; - else - return (void *) p; - } - return NULL; -} - - - -/* From https://github.com/noporpoise/sort_r */ - -/* Isaac Turner 29 April 2014 Public Domain */ - -/* - -hb_sort_r function to be exported. - -Parameters: - base is the array to be sorted - nel is the number of elements in the array - width is the size in bytes of each element of the array - compar is the comparison function - arg is a pointer to be passed to the comparison function - -void hb_sort_r(void *base, size_t nel, size_t width, - int (*compar)(const void *_a, const void *_b, void *_arg), - void *arg); -*/ - - -/* swap a, b iff a>b */ -/* __restrict is same as restrict but better support on old machines */ -static int sort_r_cmpswap(char *__restrict a, char *__restrict b, size_t w, - int (*compar)(const void *_a, const void *_b, - void *_arg), - void *arg) -{ - char tmp, *end = a+w; - if(compar(a, b, arg) > 0) { - for(; a < end; a++, b++) { tmp = *a; *a = *b; *b = tmp; } - return 1; - } - return 0; -} - -/* Note: quicksort is not stable, equivalent values may be swapped */ -static inline void sort_r_simple(void *base, size_t nel, size_t w, - int (*compar)(const void *_a, const void *_b, - void *_arg), - void *arg) -{ - char *b = (char *)base, *end = b + nel*w; - if(nel < 7) { - /* Insertion sort for arbitrarily small inputs */ - char *pi, *pj; - for(pi = b+w; pi < end; pi += w) { - for(pj = pi; pj > b && sort_r_cmpswap(pj-w,pj,w,compar,arg); pj -= w) {} - } - } - else - { - /* nel > 6; Quicksort */ - - /* Use median of first, middle and last items as pivot */ - char *x, *y, *xend, ch; - char *pl, *pr; - char *last = b+w*(nel-1), *tmp; - char *l[3]; - l[0] = b; - l[1] = b+w*(nel/2); - l[2] = last; - - if(compar(l[0],l[1],arg) > 0) { tmp=l[0]; l[0]=l[1]; l[1]=tmp; } - if(compar(l[1],l[2],arg) > 0) { - tmp=l[1]; l[1]=l[2]; l[2]=tmp; /* swap(l[1],l[2]) */ - if(compar(l[0],l[1],arg) > 0) { tmp=l[0]; l[0]=l[1]; l[1]=tmp; } - } - - /* swap l[id], l[2] to put pivot as last element */ - for(x = l[1], y = last, xend = x+w; x tables; +}; + +static int compare_entries (const void* pa, const void* pb) +{ + const auto& a = * (const hb_pair_t *) pa; + const auto& b = * (const hb_pair_t *) pb; + + /* Order by blob size first (smallest to largest) and then table tag */ + + if (a.second.order != b.second.order) + return a.second.order < b.second.order ? -1 : +1; + + if (a.second.data->length != b.second.data->length) + return a.second.data->length < b.second.data->length ? -1 : +1; + + return a.first < b.first ? -1 : a.first == b.first ? 0 : +1; +} + +static hb_face_builder_data_t * +_hb_face_builder_data_create () +{ + hb_face_builder_data_t *data = (hb_face_builder_data_t *) hb_calloc (1, sizeof (hb_face_builder_data_t)); + if (unlikely (!data)) + return nullptr; + + data->tables.init (); + + return data; +} + +static void +_hb_face_builder_data_destroy (void *user_data) +{ + hb_face_builder_data_t *data = (hb_face_builder_data_t *) user_data; + + for (auto info : data->tables.values()) + hb_blob_destroy (info.data); + + data->tables.fini (); + + hb_free (data); +} + +static hb_blob_t * +_hb_face_builder_data_reference_blob (hb_face_builder_data_t *data) +{ + + unsigned int table_count = data->tables.get_population (); + unsigned int face_length = table_count * 16 + 12; + + for (auto info : data->tables.values()) + face_length += hb_ceil_to_4 (hb_blob_get_length (info.data)); + + char *buf = (char *) hb_malloc (face_length); + if (unlikely (!buf)) + return nullptr; + + hb_serialize_context_t c (buf, face_length); + c.propagate_error (data->tables); + OT::OpenTypeFontFile *f = c.start_serialize (); + + bool is_cff = (data->tables.has (HB_TAG ('C','F','F',' ')) + || data->tables.has (HB_TAG ('C','F','F','2'))); + hb_tag_t sfnt_tag = is_cff ? OT::OpenTypeFontFile::CFFTag : OT::OpenTypeFontFile::TrueTypeTag; + + // Sort the tags so that produced face is deterministic. + hb_vector_t> sorted_entries; + data->tables.iter () | hb_sink (sorted_entries); + if (unlikely (sorted_entries.in_error ())) + { + hb_free (buf); + return nullptr; + } + + sorted_entries.qsort (compare_entries); + + bool ret = f->serialize_single (&c, + sfnt_tag, + + sorted_entries.iter() + | hb_map ([&] (hb_pair_t _) { + return hb_pair_t (_.first, _.second.data); + })); + + c.end_serialize (); + + if (unlikely (!ret)) + { + hb_free (buf); + return nullptr; + } + + return hb_blob_create (buf, face_length, HB_MEMORY_MODE_WRITABLE, buf, hb_free); +} + +static hb_blob_t * +_hb_face_builder_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) +{ + hb_face_builder_data_t *data = (hb_face_builder_data_t *) user_data; + + if (!tag) + return _hb_face_builder_data_reference_blob (data); + + return hb_blob_reference (data->tables[tag].data); +} + + +/** + * hb_face_builder_create: + * + * Creates a #hb_face_t that can be used with hb_face_builder_add_table(). + * After tables are added to the face, it can be compiled to a binary + * font file by calling hb_face_reference_blob(). + * + * Return value: (transfer full): New face. + * + * Since: 1.9.0 + **/ +hb_face_t * +hb_face_builder_create () +{ + hb_face_builder_data_t *data = _hb_face_builder_data_create (); + if (unlikely (!data)) return hb_face_get_empty (); + + return hb_face_create_for_tables (_hb_face_builder_reference_table, + data, + _hb_face_builder_data_destroy); +} + +/** + * hb_face_builder_add_table: + * @face: A face object created with hb_face_builder_create() + * @tag: The #hb_tag_t of the table to add + * @blob: The blob containing the table data to add + * + * Add table for @tag with data provided by @blob to the face. @face must + * be created using hb_face_builder_create(). + * + * Since: 1.9.0 + **/ +hb_bool_t +hb_face_builder_add_table (hb_face_t *face, hb_tag_t tag, hb_blob_t *blob) +{ + if (unlikely (face->destroy != (hb_destroy_func_t) _hb_face_builder_data_destroy)) + return false; + + if (tag == HB_MAP_VALUE_INVALID) + return false; + + hb_face_builder_data_t *data = (hb_face_builder_data_t *) face->user_data; + + hb_blob_t* previous = data->tables.get (tag).data; + if (!data->tables.set (tag, face_table_info_t {hb_blob_reference (blob), -1})) + { + hb_blob_destroy (blob); + return false; + } + + hb_blob_destroy (previous); + return true; +} + +/** + * hb_face_builder_sort_tables: + * @face: A face object created with hb_face_builder_create() + * @tags: (array zero-terminated=1): ordered list of table tags terminated by + * %HB_TAG_NONE + * + * Set the ordering of tables for serialization. Any tables not + * specified in the tags list will be ordered after the tables in + * tags, ordered by the default sort ordering. + * + * Since: 5.3.0 + **/ +void +hb_face_builder_sort_tables (hb_face_t *face, + const hb_tag_t *tags) +{ + if (unlikely (face->destroy != (hb_destroy_func_t) _hb_face_builder_data_destroy)) + return; + + hb_face_builder_data_t *data = (hb_face_builder_data_t *) face->user_data; + + // Sort all unspecified tables after any specified tables. + for (auto& info : data->tables.values_ref()) + info.order = (unsigned) -1; + + signed order = 0; + for (const hb_tag_t* tag = tags; + *tag; + tag++) + { + face_table_info_t* info; + if (!data->tables.has (*tag, &info)) continue; + info->order = order++; + } +} diff --git a/src/3rdparty/harfbuzz-ng/src/hb-face.cc b/src/3rdparty/harfbuzz-ng/src/hb-face.cc index 26fddbe5292..e340710586c 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-face.cc +++ b/src/3rdparty/harfbuzz-ng/src/hb-face.cc @@ -26,23 +26,70 @@ * Google Author(s): Behdad Esfahbod */ -#include "hb-private.hh" +#include "hb.hh" -#include "hb-face-private.hh" -#include "hb-open-file-private.hh" -#include "hb-ot-head-table.hh" -#include "hb-ot-maxp-table.hh" +#include "hb-face.hh" +#include "hb-blob.hh" +#include "hb-open-file.hh" +#include "hb-ot-face.hh" +#include "hb-ot-cmap-table.hh" +/** + * SECTION:hb-face + * @title: hb-face + * @short_description: Font face objects + * @include: hb.h + * + * A font face is an object that represents a single face from within a + * font family. + * + * More precisely, a font face represents a single face in a binary font file. + * Font faces are typically built from a binary blob and a face index. + * Font faces are used to create fonts. + * + * A font face can be created from a binary blob using hb_face_create(). + * The face index is used to select a face from a binary blob that contains + * multiple faces. For example, a binary blob that contains both a regular + * and a bold face can be used to create two font faces, one for each face + * index. + **/ + + +/** + * hb_face_count: + * @blob: a blob. + * + * Fetches the number of faces in a blob. + * + * Return value: Number of faces in @blob + * + * Since: 1.7.7 + **/ +unsigned int +hb_face_count (hb_blob_t *blob) +{ + if (unlikely (!blob)) + return 0; + + /* TODO We shouldn't be sanitizing blob. Port to run sanitizer and return if not sane. */ + /* Make API signature const after. */ + hb_blob_t *sanitized = hb_sanitize_context_t ().sanitize_blob (hb_blob_reference (blob)); + const OT::OpenTypeFontFile& ot = *sanitized->as (); + unsigned int ret = ot.get_face_count (); + hb_blob_destroy (sanitized); + + return ret; +} + /* * hb_face_t */ -const hb_face_t _hb_face_nil = { +DEFINE_NULL_INSTANCE (hb_face_t) = +{ HB_OBJECT_HEADER_STATIC, - true, /* immutable */ - nullptr, /* reference_table_func */ nullptr, /* user_data */ nullptr, /* destroy */ @@ -51,25 +98,25 @@ const hb_face_t _hb_face_nil = { 1000, /* upem */ 0, /* num_glyphs */ - { -#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID, -#include "hb-shaper-list.hh" -#undef HB_SHAPER_IMPLEMENT - }, - - nullptr, /* shape_plans */ + /* Zero for the rest is fine. */ }; /** * hb_face_create_for_tables: - * @reference_table_func: (closure user_data) (destroy destroy) (scope notified): - * @user_data: - * @destroy: + * @reference_table_func: (closure user_data) (destroy destroy) (scope notified): Table-referencing function + * @user_data: A pointer to the user data + * @destroy: (nullable): A callback to call when @data is not needed anymore * - * + * Variant of hb_face_create(), built for those cases where it is more + * convenient to provide data for individual tables instead of the whole font + * data. With the caveat that hb_face_get_table_tags() does not currently work + * with faces created this way. * - * Return value: (transfer full) + * Creates a new face object from the specified @user_data and @reference_table_func, + * with the @destroy callback. + * + * Return value: (transfer full): The new face object * * Since: 0.9.2 **/ @@ -90,8 +137,10 @@ hb_face_create_for_tables (hb_reference_table_func_t reference_table_func, face->user_data = user_data; face->destroy = destroy; - face->upem = 0; - face->num_glyphs = (unsigned int) -1; + face->num_glyphs = -1; + + face->data.init0 (face); + face->table.init0 (face); return face; } @@ -99,7 +148,7 @@ hb_face_create_for_tables (hb_reference_table_func_t reference_table_func, typedef struct hb_face_for_data_closure_t { hb_blob_t *blob; - unsigned int index; + uint16_t index; } hb_face_for_data_closure_t; static hb_face_for_data_closure_t * @@ -107,12 +156,12 @@ _hb_face_for_data_closure_create (hb_blob_t *blob, unsigned int index) { hb_face_for_data_closure_t *closure; - closure = (hb_face_for_data_closure_t *) calloc (1, sizeof (hb_face_for_data_closure_t)); + closure = (hb_face_for_data_closure_t *) hb_calloc (1, sizeof (hb_face_for_data_closure_t)); if (unlikely (!closure)) return nullptr; closure->blob = blob; - closure->index = index; + closure->index = (uint16_t) (index & 0xFFFFu); return closure; } @@ -123,7 +172,7 @@ _hb_face_for_data_closure_destroy (void *data) hb_face_for_data_closure_t *closure = (hb_face_for_data_closure_t *) data; hb_blob_destroy (closure->blob); - free (closure); + hb_free (closure); } static hb_blob_t * @@ -134,24 +183,38 @@ _hb_face_for_data_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void if (tag == HB_TAG_NONE) return hb_blob_reference (data->blob); - const OT::OpenTypeFontFile &ot_file = *OT::Sanitizer::lock_instance (data->blob); - const OT::OpenTypeFontFace &ot_face = ot_file.get_face (data->index); + const OT::OpenTypeFontFile &ot_file = *data->blob->as (); + unsigned int base_offset; + const OT::OpenTypeFontFace &ot_face = ot_file.get_face (data->index, &base_offset); const OT::OpenTypeTable &table = ot_face.get_table_by_tag (tag); - hb_blob_t *blob = hb_blob_create_sub_blob (data->blob, table.offset, table.length); + hb_blob_t *blob = hb_blob_create_sub_blob (data->blob, base_offset + table.offset, table.length); return blob; } /** - * hb_face_create: (Xconstructor) - * @blob: - * @index: + * hb_face_create: + * @blob: #hb_blob_t to work upon + * @index: The index of the face within @blob * - * + * Constructs a new face object from the specified blob and + * a face index into that blob. * - * Return value: (transfer full): + * The face index is used for blobs of file formats such as TTC and + * DFont that can contain more than one face. Face indices within + * such collections are zero-based. + * + * Note: If the blob font format is not a collection, @index + * is ignored. Otherwise, only the lower 16-bits of @index are used. + * The unmodified @index can be accessed via hb_face_get_index(). + * + * Note: The high 16-bits of @index, if non-zero, are used by + * hb_font_create() to load named-instances in variable fonts. See + * hb_font_create() for details. + * + * Return value: (transfer full): The new face object * * Since: 0.9.2 **/ @@ -164,10 +227,15 @@ hb_face_create (hb_blob_t *blob, if (unlikely (!blob)) blob = hb_blob_get_empty (); - hb_face_for_data_closure_t *closure = _hb_face_for_data_closure_create (OT::Sanitizer::sanitize (hb_blob_reference (blob)), index); + blob = hb_sanitize_context_t ().sanitize_blob (hb_blob_reference (blob)); + + hb_face_for_data_closure_t *closure = _hb_face_for_data_closure_create (blob, index); if (unlikely (!closure)) + { + hb_blob_destroy (blob); return hb_face_get_empty (); + } face = hb_face_create_for_tables (_hb_face_for_data_reference_table, closure, @@ -181,26 +249,26 @@ hb_face_create (hb_blob_t *blob, /** * hb_face_get_empty: * - * + * Fetches the singleton empty face object. * - * Return value: (transfer full) + * Return value: (transfer full): The empty face object * * Since: 0.9.2 **/ hb_face_t * -hb_face_get_empty (void) +hb_face_get_empty () { - return const_cast (&_hb_face_nil); + return const_cast (&Null (hb_face_t)); } /** * hb_face_reference: (skip) - * @face: a face. + * @face: A face object * - * + * Increases the reference count on a face object. * - * Return value: + * Return value: The @face object * * Since: 0.9.2 **/ @@ -212,9 +280,11 @@ hb_face_reference (hb_face_t *face) /** * hb_face_destroy: (skip) - * @face: a face. + * @face: A face object * - * + * Decreases the reference count on a face object. When the + * reference count reaches zero, the face is destroyed, + * freeing all memory. * * Since: 0.9.2 **/ @@ -223,35 +293,36 @@ hb_face_destroy (hb_face_t *face) { if (!hb_object_destroy (face)) return; +#ifndef HB_NO_SHAPER for (hb_face_t::plan_node_t *node = face->shape_plans; node; ) { hb_face_t::plan_node_t *next = node->next; hb_shape_plan_destroy (node->shape_plan); - free (node); + hb_free (node); node = next; } +#endif -#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_DESTROY(shaper, face); -#include "hb-shaper-list.hh" -#undef HB_SHAPER_IMPLEMENT + face->data.fini (); + face->table.fini (); if (face->destroy) face->destroy (face->user_data); - free (face); + hb_free (face); } /** * hb_face_set_user_data: (skip) - * @face: a face. - * @key: - * @data: - * @destroy: - * @replace: + * @face: A face object + * @key: The user-data key to set + * @data: A pointer to the user data + * @destroy: (nullable): A callback to call when @data is not needed anymore + * @replace: Whether to replace an existing data with the same key * - * + * Attaches a user-data key/data pair to the given face object. * - * Return value: + * Return value: `true` if success, `false` otherwise * * Since: 0.9.2 **/ @@ -267,17 +338,18 @@ hb_face_set_user_data (hb_face_t *face, /** * hb_face_get_user_data: (skip) - * @face: a face. - * @key: + * @face: A face object + * @key: The user-data key to query * - * + * Fetches the user data associated with the specified key, + * attached to the specified face object. * - * Return value: (transfer none): + * Return value: (transfer none): A pointer to the user data * * Since: 0.9.2 **/ void * -hb_face_get_user_data (hb_face_t *face, +hb_face_get_user_data (const hb_face_t *face, hb_user_data_key_t *key) { return hb_object_get_user_data (face, key); @@ -285,63 +357,69 @@ hb_face_get_user_data (hb_face_t *face, /** * hb_face_make_immutable: - * @face: a face. + * @face: A face object * - * + * Makes the given face object immutable. * * Since: 0.9.2 **/ void hb_face_make_immutable (hb_face_t *face) { - if (unlikely (hb_object_is_inert (face))) + if (hb_object_is_immutable (face)) return; - face->immutable = true; + hb_object_make_immutable (face); } /** * hb_face_is_immutable: - * @face: a face. + * @face: A face object * - * + * Tests whether the given face object is immutable. * - * Return value: + * Return value: `true` is @face is immutable, `false` otherwise * * Since: 0.9.2 **/ hb_bool_t -hb_face_is_immutable (hb_face_t *face) +hb_face_is_immutable (const hb_face_t *face) { - return face->immutable; + return hb_object_is_immutable (face); } /** * hb_face_reference_table: - * @face: a face. - * @tag: + * @face: A face object + * @tag: The #hb_tag_t of the table to query * - * + * Fetches a reference to the specified table within + * the specified face. * - * Return value: (transfer full): + * Return value: (transfer full): A pointer to the @tag table within @face * * Since: 0.9.2 **/ hb_blob_t * -hb_face_reference_table (hb_face_t *face, - hb_tag_t tag) +hb_face_reference_table (const hb_face_t *face, + hb_tag_t tag) { + if (unlikely (tag == HB_TAG_NONE)) + return hb_blob_get_empty (); + return face->reference_table (tag); } /** * hb_face_reference_blob: - * @face: a face. + * @face: A face object * - * + * Fetches a pointer to the binary blob that contains the + * specified face. Returns an empty blob if referencing face data is not + * possible. * - * Return value: (transfer full): + * Return value: (transfer full): A pointer to the blob for @face * * Since: 0.9.2 **/ @@ -353,10 +431,14 @@ hb_face_reference_blob (hb_face_t *face) /** * hb_face_set_index: - * @face: a face. - * @index: + * @face: A face object + * @index: The index to assign * - * + * Assigns the specified face-index to @face. Fails if the + * face is immutable. + * + * Note: changing the index has no effect on the face itself + * This only changes the value returned by hb_face_get_index(). * * Since: 0.9.2 **/ @@ -364,7 +446,7 @@ void hb_face_set_index (hb_face_t *face, unsigned int index) { - if (face->immutable) + if (hb_object_is_immutable (face)) return; face->index = index; @@ -372,26 +454,30 @@ hb_face_set_index (hb_face_t *face, /** * hb_face_get_index: - * @face: a face. + * @face: A face object * - * + * Fetches the face-index corresponding to the given face. * - * Return value: + * Note: face indices within a collection are zero-based. + * + * Return value: The index of @face. * * Since: 0.9.2 **/ unsigned int -hb_face_get_index (hb_face_t *face) +hb_face_get_index (const hb_face_t *face) { return face->index; } /** * hb_face_set_upem: - * @face: a face. - * @upem: + * @face: A face object + * @upem: The units-per-em value to assign * - * + * Sets the units-per-em (upem) for a face object to the specified value. + * + * This API is used in rare circumstances. * * Since: 0.9.2 **/ @@ -399,7 +485,7 @@ void hb_face_set_upem (hb_face_t *face, unsigned int upem) { - if (face->immutable) + if (hb_object_is_immutable (face)) return; face->upem = upem; @@ -407,35 +493,31 @@ hb_face_set_upem (hb_face_t *face, /** * hb_face_get_upem: - * @face: a face. + * @face: A face object * - * + * Fetches the units-per-em (UPEM) value of the specified face object. * - * Return value: + * Typical UPEM values for fonts are 1000, or 2048, but any value + * in between 16 and 16,384 is allowed for OpenType fonts. + * + * Return value: The upem value of @face * * Since: 0.9.2 **/ unsigned int -hb_face_get_upem (hb_face_t *face) +hb_face_get_upem (const hb_face_t *face) { return face->get_upem (); } -void -hb_face_t::load_upem (void) const -{ - hb_blob_t *head_blob = OT::Sanitizer::sanitize (reference_table (HB_OT_TAG_head)); - const OT::head *head_table = OT::Sanitizer::lock_instance (head_blob); - upem = head_table->get_upem (); - hb_blob_destroy (head_blob); -} - /** * hb_face_set_glyph_count: - * @face: a face. - * @glyph_count: + * @face: A face object + * @glyph_count: The glyph-count value to assign * - * + * Sets the glyph count for a face object to the specified value. + * + * This API is used in rare circumstances. * * Since: 0.9.7 **/ @@ -443,7 +525,7 @@ void hb_face_set_glyph_count (hb_face_t *face, unsigned int glyph_count) { - if (face->immutable) + if (hb_object_is_immutable (face)) return; face->num_glyphs = glyph_count; @@ -451,46 +533,42 @@ hb_face_set_glyph_count (hb_face_t *face, /** * hb_face_get_glyph_count: - * @face: a face. + * @face: A face object * - * + * Fetches the glyph-count value of the specified face object. * - * Return value: + * Return value: The glyph-count value of @face * * Since: 0.9.7 **/ unsigned int -hb_face_get_glyph_count (hb_face_t *face) +hb_face_get_glyph_count (const hb_face_t *face) { return face->get_num_glyphs (); } -void -hb_face_t::load_num_glyphs (void) const -{ - hb_blob_t *maxp_blob = OT::Sanitizer::sanitize (reference_table (HB_OT_TAG_maxp)); - const OT::maxp *maxp_table = OT::Sanitizer::lock_instance (maxp_blob); - num_glyphs = maxp_table->get_num_glyphs (); - hb_blob_destroy (maxp_blob); -} - /** * hb_face_get_table_tags: - * @face: a face. + * @face: A face object + * @start_offset: The index of first table tag to retrieve + * @table_count: (inout): Input = the maximum number of table tags to return; + * Output = the actual number of table tags returned (may be zero) + * @table_tags: (out) (array length=table_count): The array of table tags found * - * Retrieves table tags for a face, if possible. + * Fetches a list of all table tags for a face, if possible. The list returned will + * begin at the offset provided * - * Return value: total number of tables, or 0 if not possible to list. + * Return value: Total number of tables, or zero if it is not possible to list * * Since: 1.6.0 **/ unsigned int -hb_face_get_table_tags (hb_face_t *face, +hb_face_get_table_tags (const hb_face_t *face, unsigned int start_offset, unsigned int *table_count, /* IN/OUT */ hb_tag_t *table_tags /* OUT */) { - if (face->destroy != _hb_face_for_data_closure_destroy) + if (face->destroy != (hb_destroy_func_t) _hb_face_for_data_closure_destroy) { if (table_count) *table_count = 0; @@ -499,8 +577,88 @@ hb_face_get_table_tags (hb_face_t *face, hb_face_for_data_closure_t *data = (hb_face_for_data_closure_t *) face->user_data; - const OT::OpenTypeFontFile &ot_file = *OT::Sanitizer::lock_instance (data->blob); + const OT::OpenTypeFontFile &ot_file = *data->blob->as (); const OT::OpenTypeFontFace &ot_face = ot_file.get_face (data->index); return ot_face.get_table_tags (start_offset, table_count, table_tags); } + + +/* + * Character set. + */ + + +#ifndef HB_NO_FACE_COLLECT_UNICODES +/** + * hb_face_collect_unicodes: + * @face: A face object + * @out: (out): The set to add Unicode characters to + * + * Collects all of the Unicode characters covered by @face and adds + * them to the #hb_set_t set @out. + * + * Since: 1.9.0 + */ +void +hb_face_collect_unicodes (hb_face_t *face, + hb_set_t *out) +{ + face->table.cmap->collect_unicodes (out, face->get_num_glyphs ()); +} +/** + * hb_face_collect_nominal_glyph_mapping: + * @face: A face object + * @mapping: (out): The map to add Unicode-to-glyph mapping to + * @unicodes: (nullable) (out): The set to add Unicode characters to, or `NULL` + * + * Collects the mapping from Unicode characters to nominal glyphs of the @face, + * and optionally all of the Unicode characters covered by @face. + * + * Since: 7.0.0 + */ +void +hb_face_collect_nominal_glyph_mapping (hb_face_t *face, + hb_map_t *mapping, + hb_set_t *unicodes) +{ + hb_set_t stack_unicodes; + if (!unicodes) + unicodes = &stack_unicodes; + face->table.cmap->collect_mapping (unicodes, mapping, face->get_num_glyphs ()); +} +/** + * hb_face_collect_variation_selectors: + * @face: A face object + * @out: (out): The set to add Variation Selector characters to + * + * Collects all Unicode "Variation Selector" characters covered by @face and adds + * them to the #hb_set_t set @out. + * + * Since: 1.9.0 + */ +void +hb_face_collect_variation_selectors (hb_face_t *face, + hb_set_t *out) +{ + face->table.cmap->collect_variation_selectors (out); +} +/** + * hb_face_collect_variation_unicodes: + * @face: A face object + * @variation_selector: The Variation Selector to query + * @out: (out): The set to add Unicode characters to + * + * Collects all Unicode characters for @variation_selector covered by @face and adds + * them to the #hb_set_t set @out. + * + * Since: 1.9.0 + */ +void +hb_face_collect_variation_unicodes (hb_face_t *face, + hb_codepoint_t variation_selector, + hb_set_t *out) +{ + face->table.cmap->collect_variation_unicodes (variation_selector, out); +} +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-face.h b/src/3rdparty/harfbuzz-ng/src/hb-face.h index 9842d52b650..2e54ccf13b7 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-face.h +++ b/src/3rdparty/harfbuzz-ng/src/hb-face.h @@ -24,7 +24,7 @@ * Red Hat Author(s): Behdad Esfahbod */ -#ifndef HB_H_IN +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) #error "Include instead." #endif @@ -33,20 +33,45 @@ #include "hb-common.h" #include "hb-blob.h" +#include "hb-map.h" +#include "hb-set.h" HB_BEGIN_DECLS +HB_EXTERN unsigned int +hb_face_count (hb_blob_t *blob); + + /* * hb_face_t */ +/** + * hb_face_t: + * + * Data type for holding font faces. + * + **/ typedef struct hb_face_t hb_face_t; HB_EXTERN hb_face_t * hb_face_create (hb_blob_t *blob, unsigned int index); +/** + * hb_reference_table_func_t: + * @face: an #hb_face_t to reference table for + * @tag: the tag of the table to reference + * @user_data: User data pointer passed by the caller + * + * Callback function for hb_face_create_for_tables(). + * + * Return value: (transfer full): A pointer to the @tag table within @face + * + * Since: 0.9.2 + */ + typedef hb_blob_t * (*hb_reference_table_func_t) (hb_face_t *face, hb_tag_t tag, void *user_data); /* calls destroy() when not needing user_data anymore */ @@ -71,21 +96,20 @@ hb_face_set_user_data (hb_face_t *face, hb_destroy_func_t destroy, hb_bool_t replace); - HB_EXTERN void * -hb_face_get_user_data (hb_face_t *face, +hb_face_get_user_data (const hb_face_t *face, hb_user_data_key_t *key); HB_EXTERN void hb_face_make_immutable (hb_face_t *face); HB_EXTERN hb_bool_t -hb_face_is_immutable (hb_face_t *face); +hb_face_is_immutable (const hb_face_t *face); HB_EXTERN hb_blob_t * -hb_face_reference_table (hb_face_t *face, - hb_tag_t tag); +hb_face_reference_table (const hb_face_t *face, + hb_tag_t tag); HB_EXTERN hb_blob_t * hb_face_reference_blob (hb_face_t *face); @@ -95,28 +119,69 @@ hb_face_set_index (hb_face_t *face, unsigned int index); HB_EXTERN unsigned int -hb_face_get_index (hb_face_t *face); +hb_face_get_index (const hb_face_t *face); HB_EXTERN void hb_face_set_upem (hb_face_t *face, unsigned int upem); HB_EXTERN unsigned int -hb_face_get_upem (hb_face_t *face); +hb_face_get_upem (const hb_face_t *face); HB_EXTERN void hb_face_set_glyph_count (hb_face_t *face, unsigned int glyph_count); HB_EXTERN unsigned int -hb_face_get_glyph_count (hb_face_t *face); +hb_face_get_glyph_count (const hb_face_t *face); HB_EXTERN unsigned int -hb_face_get_table_tags (hb_face_t *face, +hb_face_get_table_tags (const hb_face_t *face, unsigned int start_offset, unsigned int *table_count, /* IN/OUT */ hb_tag_t *table_tags /* OUT */); + +/* + * Character set. + */ + +HB_EXTERN void +hb_face_collect_unicodes (hb_face_t *face, + hb_set_t *out); + +HB_EXTERN void +hb_face_collect_nominal_glyph_mapping (hb_face_t *face, + hb_map_t *mapping, + hb_set_t *unicodes); + +HB_EXTERN void +hb_face_collect_variation_selectors (hb_face_t *face, + hb_set_t *out); + +HB_EXTERN void +hb_face_collect_variation_unicodes (hb_face_t *face, + hb_codepoint_t variation_selector, + hb_set_t *out); + + +/* + * Builder face. + */ + +HB_EXTERN hb_face_t * +hb_face_builder_create (void); + +HB_EXTERN hb_bool_t +hb_face_builder_add_table (hb_face_t *face, + hb_tag_t tag, + hb_blob_t *blob); + +HB_EXTERN void +hb_face_builder_sort_tables (hb_face_t *face, + const hb_tag_t *tags); + + HB_END_DECLS #endif /* HB_FACE_H */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-face-private.hh b/src/3rdparty/harfbuzz-ng/src/hb-face.hh similarity index 58% rename from src/3rdparty/harfbuzz-ng/src/hb-face-private.hh rename to src/3rdparty/harfbuzz-ng/src/hb-face.hh index 43e7b1cb3ff..aff3ff0d07c 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-face-private.hh +++ b/src/3rdparty/harfbuzz-ng/src/hb-face.hh @@ -26,86 +26,86 @@ * Google Author(s): Behdad Esfahbod */ -#ifndef HB_FACE_PRIVATE_HH -#define HB_FACE_PRIVATE_HH +#ifndef HB_FACE_HH +#define HB_FACE_HH -#include "hb-private.hh" +#include "hb.hh" -#include "hb-object-private.hh" -#include "hb-shaper-private.hh" -#include "hb-shape-plan-private.hh" +#include "hb-shaper.hh" +#include "hb-shape-plan.hh" +#include "hb-ot-face.hh" /* * hb_face_t */ -struct hb_face_t { - hb_object_header_t header; - ASSERT_POD (); +#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INSTANTIATE_SHAPERS(shaper, face); +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT - hb_bool_t immutable; +struct hb_face_t +{ + hb_object_header_t header; hb_reference_table_func_t reference_table_func; void *user_data; hb_destroy_func_t destroy; unsigned int index; /* Face index in a collection, zero-based. */ - mutable unsigned int upem; /* Units-per-EM. */ - mutable unsigned int num_glyphs; /* Number of glyphs. */ + mutable hb_atomic_int_t upem; /* Units-per-EM. */ + mutable hb_atomic_int_t num_glyphs; /* Number of glyphs. */ - struct hb_shaper_data_t shaper_data; /* Various shaper data. */ - - /* Various non-shaping data. */ - /* ... */ + hb_shaper_object_dataset_t data;/* Various shaper data. */ + hb_ot_face_t table; /* All the face's tables. */ /* Cache */ - struct plan_node_t { + struct plan_node_t + { hb_shape_plan_t *shape_plan; plan_node_t *next; - } *shape_plans; + }; +#ifndef HB_NO_SHAPER + hb_atomic_ptr_t shape_plans; +#endif - - inline hb_blob_t *reference_table (hb_tag_t tag) const + hb_blob_t *reference_table (hb_tag_t tag) const { hb_blob_t *blob; if (unlikely (!reference_table_func)) return hb_blob_get_empty (); - blob = reference_table_func (/*XXX*/const_cast (this), tag, user_data); + blob = reference_table_func (/*Oh, well.*/const_cast (this), tag, user_data); if (unlikely (!blob)) return hb_blob_get_empty (); return blob; } - inline HB_PURE_FUNC unsigned int get_upem (void) const + unsigned int get_upem () const { - if (unlikely (!upem)) - load_upem (); - return upem; + unsigned int ret = upem; + if (unlikely (!ret)) + { + return load_upem (); + } + return ret; } - inline unsigned int get_num_glyphs (void) const + unsigned int get_num_glyphs () const { - if (unlikely (num_glyphs == (unsigned int) -1)) - load_num_glyphs (); - return num_glyphs; + unsigned int ret = num_glyphs; + if (unlikely (ret == UINT_MAX)) + return load_num_glyphs (); + return ret; } private: - HB_INTERNAL void load_upem (void) const; - HB_INTERNAL void load_num_glyphs (void) const; + HB_INTERNAL unsigned int load_upem () const; + HB_INTERNAL unsigned int load_num_glyphs () const; }; - -extern HB_INTERNAL const hb_face_t _hb_face_nil; - -#define HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS -#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_PROTOTYPE(shaper, face); -#include "hb-shaper-list.hh" -#undef HB_SHAPER_IMPLEMENT -#undef HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS +DECLARE_NULL_INSTANCE (hb_face_t); -#endif /* HB_FACE_PRIVATE_HH */ +#endif /* HB_FACE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-fallback-shape.cc b/src/3rdparty/harfbuzz-ng/src/hb-fallback-shape.cc index 3f09c3f5306..c54ad8764bc 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-fallback-shape.cc +++ b/src/3rdparty/harfbuzz-ng/src/hb-fallback-shape.cc @@ -24,28 +24,24 @@ * Google Author(s): Behdad Esfahbod */ -#define HB_SHAPER fallback -#include "hb-shaper-impl-private.hh" - - -HB_SHAPER_DATA_ENSURE_DEFINE(fallback, face) -HB_SHAPER_DATA_ENSURE_DEFINE(fallback, font) +#include "hb-shaper-impl.hh" +#ifndef HB_NO_FALLBACK_SHAPE /* * shaper face data */ -struct hb_fallback_shaper_face_data_t {}; +struct hb_fallback_face_data_t {}; -hb_fallback_shaper_face_data_t * +hb_fallback_face_data_t * _hb_fallback_shaper_face_data_create (hb_face_t *face HB_UNUSED) { - return (hb_fallback_shaper_face_data_t *) HB_SHAPER_DATA_SUCCEEDED; + return (hb_fallback_face_data_t *) HB_SHAPER_DATA_SUCCEEDED; } void -_hb_fallback_shaper_face_data_destroy (hb_fallback_shaper_face_data_t *data HB_UNUSED) +_hb_fallback_shaper_face_data_destroy (hb_fallback_face_data_t *data HB_UNUSED) { } @@ -54,38 +50,16 @@ _hb_fallback_shaper_face_data_destroy (hb_fallback_shaper_face_data_t *data HB_U * shaper font data */ -struct hb_fallback_shaper_font_data_t {}; +struct hb_fallback_font_data_t {}; -hb_fallback_shaper_font_data_t * +hb_fallback_font_data_t * _hb_fallback_shaper_font_data_create (hb_font_t *font HB_UNUSED) { - return (hb_fallback_shaper_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; + return (hb_fallback_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; } void -_hb_fallback_shaper_font_data_destroy (hb_fallback_shaper_font_data_t *data HB_UNUSED) -{ -} - - -/* - * shaper shape_plan data - */ - -struct hb_fallback_shaper_shape_plan_data_t {}; - -hb_fallback_shaper_shape_plan_data_t * -_hb_fallback_shaper_shape_plan_data_create (hb_shape_plan_t *shape_plan HB_UNUSED, - const hb_feature_t *user_features HB_UNUSED, - unsigned int num_user_features HB_UNUSED, - const int *coords HB_UNUSED, - unsigned int num_coords HB_UNUSED) -{ - return (hb_fallback_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED; -} - -void -_hb_fallback_shaper_shape_plan_data_destroy (hb_fallback_shaper_shape_plan_data_t *data HB_UNUSED) +_hb_fallback_shaper_font_data_destroy (hb_fallback_font_data_t *data HB_UNUSED) { } @@ -101,16 +75,6 @@ _hb_fallback_shape (hb_shape_plan_t *shape_plan HB_UNUSED, const hb_feature_t *features HB_UNUSED, unsigned int num_features HB_UNUSED) { - /* TODO - * - * - Apply fallback kern. - * - Handle Variation Selectors? - * - Apply normalization? - * - * This will make the fallback shaper into a dumb "TrueType" - * shaper which many people unfortunately still request. - */ - hb_codepoint_t space; bool has_space = (bool) font->get_nominal_glyph (' ', &space); @@ -143,7 +107,9 @@ _hb_fallback_shape (hb_shape_plan_t *shape_plan HB_UNUSED, if (HB_DIRECTION_IS_BACKWARD (direction)) hb_buffer_reverse (buffer); - buffer->safe_to_break_all (); + buffer->clear_glyph_flags (); return true; } + +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-font-private.hh b/src/3rdparty/harfbuzz-ng/src/hb-font-private.hh deleted file mode 100644 index d2801fb86ec..00000000000 --- a/src/3rdparty/harfbuzz-ng/src/hb-font-private.hh +++ /dev/null @@ -1,555 +0,0 @@ -/* - * Copyright © 2009 Red Hat, Inc. - * Copyright © 2011 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. - * - * Red Hat Author(s): Behdad Esfahbod - * Google Author(s): Behdad Esfahbod - */ - -#ifndef HB_FONT_PRIVATE_HH -#define HB_FONT_PRIVATE_HH - -#include "hb-private.hh" - -#include "hb-object-private.hh" -#include "hb-face-private.hh" -#include "hb-shaper-private.hh" - - - -/* - * hb_font_funcs_t - */ - -#define HB_FONT_FUNCS_IMPLEMENT_CALLBACKS \ - HB_FONT_FUNC_IMPLEMENT (font_h_extents) \ - HB_FONT_FUNC_IMPLEMENT (font_v_extents) \ - HB_FONT_FUNC_IMPLEMENT (nominal_glyph) \ - HB_FONT_FUNC_IMPLEMENT (variation_glyph) \ - HB_FONT_FUNC_IMPLEMENT (glyph_h_advance) \ - HB_FONT_FUNC_IMPLEMENT (glyph_v_advance) \ - HB_FONT_FUNC_IMPLEMENT (glyph_h_origin) \ - HB_FONT_FUNC_IMPLEMENT (glyph_v_origin) \ - HB_FONT_FUNC_IMPLEMENT (glyph_h_kerning) \ - HB_FONT_FUNC_IMPLEMENT (glyph_v_kerning) \ - HB_FONT_FUNC_IMPLEMENT (glyph_extents) \ - HB_FONT_FUNC_IMPLEMENT (glyph_contour_point) \ - HB_FONT_FUNC_IMPLEMENT (glyph_name) \ - HB_FONT_FUNC_IMPLEMENT (glyph_from_name) \ - /* ^--- Add new callbacks here */ - -struct hb_font_funcs_t { - hb_object_header_t header; - ASSERT_POD (); - - hb_bool_t immutable; - - struct { -#define HB_FONT_FUNC_IMPLEMENT(name) void *name; - HB_FONT_FUNCS_IMPLEMENT_CALLBACKS -#undef HB_FONT_FUNC_IMPLEMENT - } user_data; - - struct { -#define HB_FONT_FUNC_IMPLEMENT(name) hb_destroy_func_t name; - HB_FONT_FUNCS_IMPLEMENT_CALLBACKS -#undef HB_FONT_FUNC_IMPLEMENT - } destroy; - - /* Don't access these directly. Call font->get_*() instead. */ - union get_t { - struct get_funcs_t { -#define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_func_t name; - HB_FONT_FUNCS_IMPLEMENT_CALLBACKS -#undef HB_FONT_FUNC_IMPLEMENT - } f; - void (*array[VAR]) (void); - } get; -}; - - - -/* - * hb_font_t - */ - -struct hb_font_t { - hb_object_header_t header; - ASSERT_POD (); - - hb_bool_t immutable; - - hb_font_t *parent; - hb_face_t *face; - - int x_scale; - int y_scale; - - unsigned int x_ppem; - unsigned int y_ppem; - - float ptem; - - /* Font variation coordinates. */ - unsigned int num_coords; - int *coords; - - hb_font_funcs_t *klass; - void *user_data; - hb_destroy_func_t destroy; - - struct hb_shaper_data_t shaper_data; - - - /* Convert from font-space to user-space */ - inline int dir_scale (hb_direction_t direction) - { return HB_DIRECTION_IS_VERTICAL(direction) ? y_scale : x_scale; } - inline hb_position_t em_scale_x (int16_t v) { return em_scale (v, x_scale); } - inline hb_position_t em_scale_y (int16_t v) { return em_scale (v, y_scale); } - inline hb_position_t em_scalef_x (float v) { return em_scalef (v, this->x_scale); } - inline hb_position_t em_scalef_y (float v) { return em_scalef (v, this->y_scale); } - inline hb_position_t em_scale_dir (int16_t v, hb_direction_t direction) - { return em_scale (v, dir_scale (direction)); } - - /* Convert from parent-font user-space to our user-space */ - inline hb_position_t parent_scale_x_distance (hb_position_t v) { - if (unlikely (parent && parent->x_scale != x_scale)) - return (hb_position_t) (v * (int64_t) this->x_scale / this->parent->x_scale); - return v; - } - inline hb_position_t parent_scale_y_distance (hb_position_t v) { - if (unlikely (parent && parent->y_scale != y_scale)) - return (hb_position_t) (v * (int64_t) this->y_scale / this->parent->y_scale); - return v; - } - inline hb_position_t parent_scale_x_position (hb_position_t v) { - return parent_scale_x_distance (v); - } - inline hb_position_t parent_scale_y_position (hb_position_t v) { - return parent_scale_y_distance (v); - } - - inline void parent_scale_distance (hb_position_t *x, hb_position_t *y) { - *x = parent_scale_x_distance (*x); - *y = parent_scale_y_distance (*y); - } - inline void parent_scale_position (hb_position_t *x, hb_position_t *y) { - *x = parent_scale_x_position (*x); - *y = parent_scale_y_position (*y); - } - - - /* Public getters */ - - HB_INTERNAL bool has_func (unsigned int i); - - /* has_* ... */ -#define HB_FONT_FUNC_IMPLEMENT(name) \ - bool \ - has_##name##_func (void) \ - { \ - hb_font_funcs_t *funcs = this->klass; \ - unsigned int i = offsetof (hb_font_funcs_t::get_t::get_funcs_t, name) / sizeof (funcs->get.array[0]); \ - return has_func (i); \ - } - HB_FONT_FUNCS_IMPLEMENT_CALLBACKS -#undef HB_FONT_FUNC_IMPLEMENT - - inline hb_bool_t get_font_h_extents (hb_font_extents_t *extents) - { - memset (extents, 0, sizeof (*extents)); - return klass->get.f.font_h_extents (this, user_data, - extents, - klass->user_data.font_h_extents); - } - inline hb_bool_t get_font_v_extents (hb_font_extents_t *extents) - { - memset (extents, 0, sizeof (*extents)); - return klass->get.f.font_v_extents (this, user_data, - extents, - klass->user_data.font_v_extents); - } - - inline bool has_glyph (hb_codepoint_t unicode) - { - hb_codepoint_t glyph; - return get_nominal_glyph (unicode, &glyph); - } - - inline hb_bool_t get_nominal_glyph (hb_codepoint_t unicode, - hb_codepoint_t *glyph) - { - *glyph = 0; - return klass->get.f.nominal_glyph (this, user_data, - unicode, glyph, - klass->user_data.nominal_glyph); - } - - inline hb_bool_t get_variation_glyph (hb_codepoint_t unicode, hb_codepoint_t variation_selector, - hb_codepoint_t *glyph) - { - *glyph = 0; - return klass->get.f.variation_glyph (this, user_data, - unicode, variation_selector, glyph, - klass->user_data.variation_glyph); - } - - inline hb_position_t get_glyph_h_advance (hb_codepoint_t glyph) - { - return klass->get.f.glyph_h_advance (this, user_data, - glyph, - klass->user_data.glyph_h_advance); - } - - inline hb_position_t get_glyph_v_advance (hb_codepoint_t glyph) - { - return klass->get.f.glyph_v_advance (this, user_data, - glyph, - klass->user_data.glyph_v_advance); - } - - inline hb_bool_t get_glyph_h_origin (hb_codepoint_t glyph, - hb_position_t *x, hb_position_t *y) - { - *x = *y = 0; - return klass->get.f.glyph_h_origin (this, user_data, - glyph, x, y, - klass->user_data.glyph_h_origin); - } - - inline hb_bool_t get_glyph_v_origin (hb_codepoint_t glyph, - hb_position_t *x, hb_position_t *y) - { - *x = *y = 0; - return klass->get.f.glyph_v_origin (this, user_data, - glyph, x, y, - klass->user_data.glyph_v_origin); - } - - inline hb_position_t get_glyph_h_kerning (hb_codepoint_t left_glyph, hb_codepoint_t right_glyph) - { - return klass->get.f.glyph_h_kerning (this, user_data, - left_glyph, right_glyph, - klass->user_data.glyph_h_kerning); - } - - inline hb_position_t get_glyph_v_kerning (hb_codepoint_t top_glyph, hb_codepoint_t bottom_glyph) - { - return klass->get.f.glyph_v_kerning (this, user_data, - top_glyph, bottom_glyph, - klass->user_data.glyph_v_kerning); - } - - inline hb_bool_t get_glyph_extents (hb_codepoint_t glyph, - hb_glyph_extents_t *extents) - { - memset (extents, 0, sizeof (*extents)); - return klass->get.f.glyph_extents (this, user_data, - glyph, - extents, - klass->user_data.glyph_extents); - } - - inline hb_bool_t get_glyph_contour_point (hb_codepoint_t glyph, unsigned int point_index, - hb_position_t *x, hb_position_t *y) - { - *x = *y = 0; - return klass->get.f.glyph_contour_point (this, user_data, - glyph, point_index, - x, y, - klass->user_data.glyph_contour_point); - } - - inline hb_bool_t get_glyph_name (hb_codepoint_t glyph, - char *name, unsigned int size) - { - if (size) *name = '\0'; - return klass->get.f.glyph_name (this, user_data, - glyph, - name, size, - klass->user_data.glyph_name); - } - - inline hb_bool_t get_glyph_from_name (const char *name, int len, /* -1 means nul-terminated */ - hb_codepoint_t *glyph) - { - *glyph = 0; - if (len == -1) len = strlen (name); - return klass->get.f.glyph_from_name (this, user_data, - name, len, - glyph, - klass->user_data.glyph_from_name); - } - - - /* A bit higher-level, and with fallback */ - - inline void get_h_extents_with_fallback (hb_font_extents_t *extents) - { - if (!get_font_h_extents (extents)) - { - extents->ascender = y_scale * .8; - extents->descender = extents->ascender - y_scale; - extents->line_gap = 0; - } - } - inline void get_v_extents_with_fallback (hb_font_extents_t *extents) - { - if (!get_font_v_extents (extents)) - { - extents->ascender = x_scale / 2; - extents->descender = extents->ascender - x_scale; - extents->line_gap = 0; - } - } - - inline void get_extents_for_direction (hb_direction_t direction, - hb_font_extents_t *extents) - { - if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) - get_h_extents_with_fallback (extents); - else - get_v_extents_with_fallback (extents); - } - - inline void get_glyph_advance_for_direction (hb_codepoint_t glyph, - hb_direction_t direction, - hb_position_t *x, hb_position_t *y) - { - if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) { - *x = get_glyph_h_advance (glyph); - *y = 0; - } else { - *x = 0; - *y = get_glyph_v_advance (glyph); - } - } - - inline void guess_v_origin_minus_h_origin (hb_codepoint_t glyph, - hb_position_t *x, hb_position_t *y) - { - *x = get_glyph_h_advance (glyph) / 2; - - /* TODO cache this somehow?! */ - hb_font_extents_t extents; - get_h_extents_with_fallback (&extents); - *y = extents.ascender; - } - - inline void get_glyph_h_origin_with_fallback (hb_codepoint_t glyph, - hb_position_t *x, hb_position_t *y) - { - if (!get_glyph_h_origin (glyph, x, y) && - get_glyph_v_origin (glyph, x, y)) - { - hb_position_t dx, dy; - guess_v_origin_minus_h_origin (glyph, &dx, &dy); - *x -= dx; *y -= dy; - } - } - inline void get_glyph_v_origin_with_fallback (hb_codepoint_t glyph, - hb_position_t *x, hb_position_t *y) - { - if (!get_glyph_v_origin (glyph, x, y) && - get_glyph_h_origin (glyph, x, y)) - { - hb_position_t dx, dy; - guess_v_origin_minus_h_origin (glyph, &dx, &dy); - *x += dx; *y += dy; - } - } - - inline void get_glyph_origin_for_direction (hb_codepoint_t glyph, - hb_direction_t direction, - hb_position_t *x, hb_position_t *y) - { - if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) - get_glyph_h_origin_with_fallback (glyph, x, y); - else - get_glyph_v_origin_with_fallback (glyph, x, y); - } - - inline void add_glyph_h_origin (hb_codepoint_t glyph, - hb_position_t *x, hb_position_t *y) - { - hb_position_t origin_x, origin_y; - - get_glyph_h_origin_with_fallback (glyph, &origin_x, &origin_y); - - *x += origin_x; - *y += origin_y; - } - inline void add_glyph_v_origin (hb_codepoint_t glyph, - hb_position_t *x, hb_position_t *y) - { - hb_position_t origin_x, origin_y; - - get_glyph_v_origin_with_fallback (glyph, &origin_x, &origin_y); - - *x += origin_x; - *y += origin_y; - } - inline void add_glyph_origin_for_direction (hb_codepoint_t glyph, - hb_direction_t direction, - hb_position_t *x, hb_position_t *y) - { - hb_position_t origin_x, origin_y; - - get_glyph_origin_for_direction (glyph, direction, &origin_x, &origin_y); - - *x += origin_x; - *y += origin_y; - } - - inline void subtract_glyph_h_origin (hb_codepoint_t glyph, - hb_position_t *x, hb_position_t *y) - { - hb_position_t origin_x, origin_y; - - get_glyph_h_origin_with_fallback (glyph, &origin_x, &origin_y); - - *x -= origin_x; - *y -= origin_y; - } - inline void subtract_glyph_v_origin (hb_codepoint_t glyph, - hb_position_t *x, hb_position_t *y) - { - hb_position_t origin_x, origin_y; - - get_glyph_v_origin_with_fallback (glyph, &origin_x, &origin_y); - - *x -= origin_x; - *y -= origin_y; - } - inline void subtract_glyph_origin_for_direction (hb_codepoint_t glyph, - hb_direction_t direction, - hb_position_t *x, hb_position_t *y) - { - hb_position_t origin_x, origin_y; - - get_glyph_origin_for_direction (glyph, direction, &origin_x, &origin_y); - - *x -= origin_x; - *y -= origin_y; - } - - inline void get_glyph_kerning_for_direction (hb_codepoint_t first_glyph, hb_codepoint_t second_glyph, - hb_direction_t direction, - hb_position_t *x, hb_position_t *y) - { - if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) { - *x = get_glyph_h_kerning (first_glyph, second_glyph); - *y = 0; - } else { - *x = 0; - *y = get_glyph_v_kerning (first_glyph, second_glyph); - } - } - - inline hb_bool_t get_glyph_extents_for_origin (hb_codepoint_t glyph, - hb_direction_t direction, - hb_glyph_extents_t *extents) - { - hb_bool_t ret = get_glyph_extents (glyph, extents); - - if (ret) - subtract_glyph_origin_for_direction (glyph, direction, &extents->x_bearing, &extents->y_bearing); - - return ret; - } - - inline hb_bool_t get_glyph_contour_point_for_origin (hb_codepoint_t glyph, unsigned int point_index, - hb_direction_t direction, - hb_position_t *x, hb_position_t *y) - { - hb_bool_t ret = get_glyph_contour_point (glyph, point_index, x, y); - - if (ret) - subtract_glyph_origin_for_direction (glyph, direction, x, y); - - return ret; - } - - /* Generates gidDDD if glyph has no name. */ - inline void - glyph_to_string (hb_codepoint_t glyph, - char *s, unsigned int size) - { - if (get_glyph_name (glyph, s, size)) return; - - if (size && snprintf (s, size, "gid%u", glyph) < 0) - *s = '\0'; - } - - /* Parses gidDDD and uniUUUU strings automatically. */ - inline hb_bool_t - glyph_from_string (const char *s, int len, /* -1 means nul-terminated */ - hb_codepoint_t *glyph) - { - if (get_glyph_from_name (s, len, glyph)) return true; - - if (len == -1) len = strlen (s); - - /* Straight glyph index. */ - if (hb_codepoint_parse (s, len, 10, glyph)) - return true; - - if (len > 3) - { - /* gidDDD syntax for glyph indices. */ - if (0 == strncmp (s, "gid", 3) && - hb_codepoint_parse (s + 3, len - 3, 10, glyph)) - return true; - - /* uniUUUU and other Unicode character indices. */ - hb_codepoint_t unichar; - if (0 == strncmp (s, "uni", 3) && - hb_codepoint_parse (s + 3, len - 3, 16, &unichar) && - get_nominal_glyph (unichar, glyph)) - return true; - } - - return false; - } - - inline hb_position_t em_scale (int16_t v, int scale) - { - int upem = face->get_upem (); - int64_t scaled = v * (int64_t) scale; - scaled += scaled >= 0 ? upem/2 : -upem/2; /* Round. */ - return (hb_position_t) (scaled / upem); - } - inline hb_position_t em_scalef (float v, int scale) - { - return (hb_position_t) (v * scale / face->get_upem ()); - } -}; - -#define HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS -#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_PROTOTYPE(shaper, font); -#include "hb-shaper-list.hh" -#undef HB_SHAPER_IMPLEMENT -#undef HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS - - -#endif /* HB_FONT_PRIVATE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-font.cc b/src/3rdparty/harfbuzz-ng/src/hb-font.cc index f3534b686bf..00f1f6d3825 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-font.cc +++ b/src/3rdparty/harfbuzz-ng/src/hb-font.cc @@ -26,9 +26,45 @@ * Google Author(s): Behdad Esfahbod */ -#include "hb-private.hh" +#include "hb.hh" -#include "hb-font-private.hh" +#include "hb-font.hh" +#include "hb-draw.hh" +#include "hb-paint.hh" +#include "hb-machinery.hh" + +#include "hb-ot.h" + +#include "hb-ot-var-avar-table.hh" +#include "hb-ot-var-fvar-table.hh" + + +/** + * SECTION:hb-font + * @title: hb-font + * @short_description: Font objects + * @include: hb.h + * + * Functions for working with font objects. + * + * A font object represents a font face at a specific size and with + * certain other parameters (pixels-per-em, points-per-em, variation + * settings) specified. Font objects are created from font face + * objects, and are used as input to hb_shape(), among other things. + * + * Client programs can optionally pass in their own functions that + * implement the basic, lower-level queries of font objects. This set + * of font functions is defined by the virtual methods in + * #hb_font_funcs_t. + * + * HarfBuzz provides a built-in set of lightweight default + * functions for each method in #hb_font_funcs_t. + * + * The default font functions are implemented in terms of the + * #hb_font_funcs_t methods of the parent font object. This allows + * client programs to override only the methods they need to, and + * otherwise inherit the parent font's implementation, if any. + **/ /* @@ -36,149 +72,267 @@ */ static hb_bool_t -hb_font_get_font_h_extents_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - hb_font_extents_t *metrics, - void *user_data HB_UNUSED) +hb_font_get_font_h_extents_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_font_extents_t *extents, + void *user_data HB_UNUSED) { - memset (metrics, 0, sizeof (*metrics)); + hb_memset (extents, 0, sizeof (*extents)); return false; } + static hb_bool_t -hb_font_get_font_h_extents_parent (hb_font_t *font, - void *font_data HB_UNUSED, - hb_font_extents_t *metrics, - void *user_data HB_UNUSED) +hb_font_get_font_h_extents_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_font_extents_t *extents, + void *user_data HB_UNUSED) { - hb_bool_t ret = font->parent->get_font_h_extents (metrics); + hb_bool_t ret = font->parent->get_font_h_extents (extents); if (ret) { - metrics->ascender = font->parent_scale_y_distance (metrics->ascender); - metrics->descender = font->parent_scale_y_distance (metrics->descender); - metrics->line_gap = font->parent_scale_y_distance (metrics->line_gap); + extents->ascender = font->parent_scale_y_distance (extents->ascender); + extents->descender = font->parent_scale_y_distance (extents->descender); + extents->line_gap = font->parent_scale_y_distance (extents->line_gap); } return ret; } static hb_bool_t -hb_font_get_font_v_extents_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - hb_font_extents_t *metrics, - void *user_data HB_UNUSED) +hb_font_get_font_v_extents_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_font_extents_t *extents, + void *user_data HB_UNUSED) { - memset (metrics, 0, sizeof (*metrics)); + hb_memset (extents, 0, sizeof (*extents)); return false; } + static hb_bool_t -hb_font_get_font_v_extents_parent (hb_font_t *font, - void *font_data HB_UNUSED, - hb_font_extents_t *metrics, - void *user_data HB_UNUSED) +hb_font_get_font_v_extents_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_font_extents_t *extents, + void *user_data HB_UNUSED) { - hb_bool_t ret = font->parent->get_font_v_extents (metrics); + hb_bool_t ret = font->parent->get_font_v_extents (extents); if (ret) { - metrics->ascender = font->parent_scale_x_distance (metrics->ascender); - metrics->descender = font->parent_scale_x_distance (metrics->descender); - metrics->line_gap = font->parent_scale_x_distance (metrics->line_gap); + extents->ascender = font->parent_scale_x_distance (extents->ascender); + extents->descender = font->parent_scale_x_distance (extents->descender); + extents->line_gap = font->parent_scale_x_distance (extents->line_gap); } return ret; } static hb_bool_t -hb_font_get_nominal_glyph_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - hb_codepoint_t unicode, +hb_font_get_nominal_glyph_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t unicode HB_UNUSED, hb_codepoint_t *glyph, - void *user_data HB_UNUSED) + void *user_data HB_UNUSED) { *glyph = 0; return false; } + static hb_bool_t -hb_font_get_nominal_glyph_parent (hb_font_t *font, - void *font_data HB_UNUSED, - hb_codepoint_t unicode, - hb_codepoint_t *glyph, - void *user_data HB_UNUSED) +hb_font_get_nominal_glyph_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t unicode, + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) { + if (font->has_nominal_glyphs_func_set ()) + { + return font->get_nominal_glyphs (1, &unicode, 0, glyph, 0); + } return font->parent->get_nominal_glyph (unicode, glyph); } +#define hb_font_get_nominal_glyphs_nil hb_font_get_nominal_glyphs_default + +static unsigned int +hb_font_get_nominal_glyphs_default (hb_font_t *font, + void *font_data HB_UNUSED, + unsigned int count, + const hb_codepoint_t *first_unicode, + unsigned int unicode_stride, + hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + void *user_data HB_UNUSED) +{ + if (font->has_nominal_glyph_func_set ()) + { + for (unsigned int i = 0; i < count; i++) + { + if (!font->get_nominal_glyph (*first_unicode, first_glyph)) + return i; + + first_unicode = &StructAtOffsetUnaligned (first_unicode, unicode_stride); + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + } + return count; + } + + return font->parent->get_nominal_glyphs (count, + first_unicode, unicode_stride, + first_glyph, glyph_stride); +} + static hb_bool_t -hb_font_get_variation_glyph_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - hb_codepoint_t unicode, - hb_codepoint_t variation_selector, +hb_font_get_variation_glyph_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t unicode HB_UNUSED, + hb_codepoint_t variation_selector HB_UNUSED, hb_codepoint_t *glyph, - void *user_data HB_UNUSED) + void *user_data HB_UNUSED) { *glyph = 0; return false; } + static hb_bool_t -hb_font_get_variation_glyph_parent (hb_font_t *font, - void *font_data HB_UNUSED, - hb_codepoint_t unicode, - hb_codepoint_t variation_selector, - hb_codepoint_t *glyph, - void *user_data HB_UNUSED) +hb_font_get_variation_glyph_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t unicode, + hb_codepoint_t variation_selector, + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) { return font->parent->get_variation_glyph (unicode, variation_selector, glyph); } static hb_position_t -hb_font_get_glyph_h_advance_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - void *user_data HB_UNUSED) +hb_font_get_glyph_h_advance_nil (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph HB_UNUSED, + void *user_data HB_UNUSED) { return font->x_scale; } + static hb_position_t -hb_font_get_glyph_h_advance_parent (hb_font_t *font, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - void *user_data HB_UNUSED) +hb_font_get_glyph_h_advance_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + void *user_data HB_UNUSED) { + if (font->has_glyph_h_advances_func_set ()) + { + hb_position_t ret; + font->get_glyph_h_advances (1, &glyph, 0, &ret, 0); + return ret; + } return font->parent_scale_x_distance (font->parent->get_glyph_h_advance (glyph)); } static hb_position_t -hb_font_get_glyph_v_advance_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - void *user_data HB_UNUSED) +hb_font_get_glyph_v_advance_nil (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph HB_UNUSED, + void *user_data HB_UNUSED) { /* TODO use font_extents.ascender+descender */ return font->y_scale; } + static hb_position_t -hb_font_get_glyph_v_advance_parent (hb_font_t *font, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - void *user_data HB_UNUSED) +hb_font_get_glyph_v_advance_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + void *user_data HB_UNUSED) { + if (font->has_glyph_v_advances_func_set ()) + { + hb_position_t ret; + font->get_glyph_v_advances (1, &glyph, 0, &ret, 0); + return ret; + } return font->parent_scale_y_distance (font->parent->get_glyph_v_advance (glyph)); } +#define hb_font_get_glyph_h_advances_nil hb_font_get_glyph_h_advances_default + +static void +hb_font_get_glyph_h_advances_default (hb_font_t* font, + void* font_data HB_UNUSED, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + hb_position_t *first_advance, + unsigned int advance_stride, + void *user_data HB_UNUSED) +{ + if (font->has_glyph_h_advance_func_set ()) + { + for (unsigned int i = 0; i < count; i++) + { + *first_advance = font->get_glyph_h_advance (*first_glyph); + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + return; + } + + font->parent->get_glyph_h_advances (count, + first_glyph, glyph_stride, + first_advance, advance_stride); + for (unsigned int i = 0; i < count; i++) + { + *first_advance = font->parent_scale_x_distance (*first_advance); + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } +} + +#define hb_font_get_glyph_v_advances_nil hb_font_get_glyph_v_advances_default +static void +hb_font_get_glyph_v_advances_default (hb_font_t* font, + void* font_data HB_UNUSED, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + hb_position_t *first_advance, + unsigned int advance_stride, + void *user_data HB_UNUSED) +{ + if (font->has_glyph_v_advance_func_set ()) + { + for (unsigned int i = 0; i < count; i++) + { + *first_advance = font->get_glyph_v_advance (*first_glyph); + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + return; + } + + font->parent->get_glyph_v_advances (count, + first_glyph, glyph_stride, + first_advance, advance_stride); + for (unsigned int i = 0; i < count; i++) + { + *first_advance = font->parent_scale_y_distance (*first_advance); + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } +} + static hb_bool_t -hb_font_get_glyph_h_origin_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - hb_position_t *x, - hb_position_t *y, - void *user_data HB_UNUSED) +hb_font_get_glyph_h_origin_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t glyph HB_UNUSED, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) { *x = *y = 0; return true; } + static hb_bool_t -hb_font_get_glyph_h_origin_parent (hb_font_t *font, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - hb_position_t *x, - hb_position_t *y, - void *user_data HB_UNUSED) +hb_font_get_glyph_h_origin_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) { hb_bool_t ret = font->parent->get_glyph_h_origin (glyph, x, y); if (ret) @@ -187,23 +341,24 @@ hb_font_get_glyph_h_origin_parent (hb_font_t *font, } static hb_bool_t -hb_font_get_glyph_v_origin_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - hb_position_t *x, - hb_position_t *y, - void *user_data HB_UNUSED) +hb_font_get_glyph_v_origin_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t glyph HB_UNUSED, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) { *x = *y = 0; return false; } + static hb_bool_t -hb_font_get_glyph_v_origin_parent (hb_font_t *font, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - hb_position_t *x, - hb_position_t *y, - void *user_data HB_UNUSED) +hb_font_get_glyph_v_origin_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) { hb_bool_t ret = font->parent->get_glyph_v_origin (glyph, x, y); if (ret) @@ -212,59 +367,64 @@ hb_font_get_glyph_v_origin_parent (hb_font_t *font, } static hb_position_t -hb_font_get_glyph_h_kerning_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - hb_codepoint_t left_glyph, - hb_codepoint_t right_glyph, - void *user_data HB_UNUSED) +hb_font_get_glyph_h_kerning_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t left_glyph HB_UNUSED, + hb_codepoint_t right_glyph HB_UNUSED, + void *user_data HB_UNUSED) { return 0; } + static hb_position_t -hb_font_get_glyph_h_kerning_parent (hb_font_t *font, - void *font_data HB_UNUSED, - hb_codepoint_t left_glyph, - hb_codepoint_t right_glyph, - void *user_data HB_UNUSED) +hb_font_get_glyph_h_kerning_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t left_glyph, + hb_codepoint_t right_glyph, + void *user_data HB_UNUSED) { return font->parent_scale_x_distance (font->parent->get_glyph_h_kerning (left_glyph, right_glyph)); } +#ifndef HB_DISABLE_DEPRECATED static hb_position_t -hb_font_get_glyph_v_kerning_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - hb_codepoint_t top_glyph, - hb_codepoint_t bottom_glyph, - void *user_data HB_UNUSED) +hb_font_get_glyph_v_kerning_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t top_glyph HB_UNUSED, + hb_codepoint_t bottom_glyph HB_UNUSED, + void *user_data HB_UNUSED) { return 0; } + static hb_position_t -hb_font_get_glyph_v_kerning_parent (hb_font_t *font, - void *font_data HB_UNUSED, - hb_codepoint_t top_glyph, - hb_codepoint_t bottom_glyph, - void *user_data HB_UNUSED) +hb_font_get_glyph_v_kerning_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t top_glyph, + hb_codepoint_t bottom_glyph, + void *user_data HB_UNUSED) { return font->parent_scale_y_distance (font->parent->get_glyph_v_kerning (top_glyph, bottom_glyph)); } +#endif static hb_bool_t -hb_font_get_glyph_extents_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, +hb_font_get_glyph_extents_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t glyph HB_UNUSED, hb_glyph_extents_t *extents, - void *user_data HB_UNUSED) + void *user_data HB_UNUSED) { - memset (extents, 0, sizeof (*extents)); + hb_memset (extents, 0, sizeof (*extents)); return false; } + static hb_bool_t -hb_font_get_glyph_extents_parent (hb_font_t *font, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - hb_glyph_extents_t *extents, - void *user_data HB_UNUSED) +hb_font_get_glyph_extents_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents, + void *user_data HB_UNUSED) { hb_bool_t ret = font->parent->get_glyph_extents (glyph, extents); if (ret) { @@ -275,25 +435,26 @@ hb_font_get_glyph_extents_parent (hb_font_t *font, } static hb_bool_t -hb_font_get_glyph_contour_point_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - unsigned int point_index, - hb_position_t *x, - hb_position_t *y, - void *user_data HB_UNUSED) +hb_font_get_glyph_contour_point_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t glyph HB_UNUSED, + unsigned int point_index HB_UNUSED, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) { *x = *y = 0; return false; } + static hb_bool_t -hb_font_get_glyph_contour_point_parent (hb_font_t *font, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - unsigned int point_index, - hb_position_t *x, - hb_position_t *y, - void *user_data HB_UNUSED) +hb_font_get_glyph_contour_point_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + unsigned int point_index, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) { hb_bool_t ret = font->parent->get_glyph_contour_point (glyph, point_index, x, y); if (ret) @@ -302,86 +463,245 @@ hb_font_get_glyph_contour_point_parent (hb_font_t *font, } static hb_bool_t -hb_font_get_glyph_name_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - char *name, unsigned int size, - void *user_data HB_UNUSED) +hb_font_get_glyph_name_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t glyph HB_UNUSED, + char *name, + unsigned int size, + void *user_data HB_UNUSED) { if (size) *name = '\0'; return false; } + static hb_bool_t -hb_font_get_glyph_name_parent (hb_font_t *font, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - char *name, unsigned int size, - void *user_data HB_UNUSED) +hb_font_get_glyph_name_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + char *name, + unsigned int size, + void *user_data HB_UNUSED) { return font->parent->get_glyph_name (glyph, name, size); } static hb_bool_t -hb_font_get_glyph_from_name_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - const char *name, int len, /* -1 means nul-terminated */ +hb_font_get_glyph_from_name_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + const char *name HB_UNUSED, + int len HB_UNUSED, /* -1 means nul-terminated */ hb_codepoint_t *glyph, - void *user_data HB_UNUSED) + void *user_data HB_UNUSED) { *glyph = 0; return false; } + static hb_bool_t -hb_font_get_glyph_from_name_parent (hb_font_t *font, - void *font_data HB_UNUSED, - const char *name, int len, /* -1 means nul-terminated */ - hb_codepoint_t *glyph, - void *user_data HB_UNUSED) +hb_font_get_glyph_from_name_default (hb_font_t *font, + void *font_data HB_UNUSED, + const char *name, + int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) { return font->parent->get_glyph_from_name (name, len, glyph); } -static const hb_font_funcs_t _hb_font_funcs_nil = { +static void +hb_font_draw_glyph_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, + void *draw_data, + void *user_data HB_UNUSED) +{ +} + +static void +hb_font_paint_glyph_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t glyph HB_UNUSED, + hb_paint_funcs_t *paint_funcs HB_UNUSED, + void *paint_data HB_UNUSED, + unsigned int palette HB_UNUSED, + hb_color_t foreground HB_UNUSED, + void *user_data HB_UNUSED) +{ +} + +typedef struct hb_font_draw_glyph_default_adaptor_t { + hb_draw_funcs_t *draw_funcs; + void *draw_data; + float x_scale; + float y_scale; + float slant; +} hb_font_draw_glyph_default_adaptor_t; + +static void +hb_draw_move_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *draw_data, + hb_draw_state_t *st, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + hb_font_draw_glyph_default_adaptor_t *adaptor = (hb_font_draw_glyph_default_adaptor_t *) draw_data; + float x_scale = adaptor->x_scale; + float y_scale = adaptor->y_scale; + float slant = adaptor->slant; + + adaptor->draw_funcs->emit_move_to (adaptor->draw_data, *st, + x_scale * to_x + slant * to_y, y_scale * to_y); +} + +static void +hb_draw_line_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data, + hb_draw_state_t *st, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + hb_font_draw_glyph_default_adaptor_t *adaptor = (hb_font_draw_glyph_default_adaptor_t *) draw_data; + float x_scale = adaptor->x_scale; + float y_scale = adaptor->y_scale; + float slant = adaptor->slant; + + st->current_x = st->current_x * x_scale + st->current_y * slant; + st->current_y = st->current_y * y_scale; + + adaptor->draw_funcs->emit_line_to (adaptor->draw_data, *st, + x_scale * to_x + slant * to_y, y_scale * to_y); +} + +static void +hb_draw_quadratic_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data, + hb_draw_state_t *st, + float control_x, float control_y, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + hb_font_draw_glyph_default_adaptor_t *adaptor = (hb_font_draw_glyph_default_adaptor_t *) draw_data; + float x_scale = adaptor->x_scale; + float y_scale = adaptor->y_scale; + float slant = adaptor->slant; + + st->current_x = st->current_x * x_scale + st->current_y * slant; + st->current_y = st->current_y * y_scale; + + adaptor->draw_funcs->emit_quadratic_to (adaptor->draw_data, *st, + x_scale * control_x + slant * control_y, y_scale * control_y, + x_scale * to_x + slant * to_y, y_scale * to_y); +} + +static void +hb_draw_cubic_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data, + hb_draw_state_t *st, + float control1_x, float control1_y, + float control2_x, float control2_y, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + hb_font_draw_glyph_default_adaptor_t *adaptor = (hb_font_draw_glyph_default_adaptor_t *) draw_data; + float x_scale = adaptor->x_scale; + float y_scale = adaptor->y_scale; + float slant = adaptor->slant; + + st->current_x = st->current_x * x_scale + st->current_y * slant; + st->current_y = st->current_y * y_scale; + + adaptor->draw_funcs->emit_cubic_to (adaptor->draw_data, *st, + x_scale * control1_x + slant * control1_y, y_scale * control1_y, + x_scale * control2_x + slant * control2_y, y_scale * control2_y, + x_scale * to_x + slant * to_y, y_scale * to_y); +} + +static void +hb_draw_close_path_default (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data, + hb_draw_state_t *st, + void *user_data HB_UNUSED) +{ + hb_font_draw_glyph_default_adaptor_t *adaptor = (hb_font_draw_glyph_default_adaptor_t *) draw_data; + + adaptor->draw_funcs->emit_close_path (adaptor->draw_data, *st); +} + +static const hb_draw_funcs_t _hb_draw_funcs_default = { HB_OBJECT_HEADER_STATIC, - true, /* immutable */ + { +#define HB_DRAW_FUNC_IMPLEMENT(name) hb_draw_##name##_default, + HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_DRAW_FUNC_IMPLEMENT + } +}; - { -#define HB_FONT_FUNC_IMPLEMENT(name) nullptr, - HB_FONT_FUNCS_IMPLEMENT_CALLBACKS -#undef HB_FONT_FUNC_IMPLEMENT - }, - { -#define HB_FONT_FUNC_IMPLEMENT(name) nullptr, - HB_FONT_FUNCS_IMPLEMENT_CALLBACKS -#undef HB_FONT_FUNC_IMPLEMENT - }, +static void +hb_font_draw_glyph_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, + void *draw_data, + void *user_data HB_UNUSED) +{ + hb_font_draw_glyph_default_adaptor_t adaptor = { + draw_funcs, + draw_data, + font->parent->x_scale ? (float) font->x_scale / (float) font->parent->x_scale : 0.f, + font->parent->y_scale ? (float) font->y_scale / (float) font->parent->y_scale : 0.f, + font->parent->y_scale ? (font->slant - font->parent->slant) * + (float) font->x_scale / (float) font->parent->y_scale : 0.f + }; + + font->parent->draw_glyph (glyph, + const_cast (&_hb_draw_funcs_default), + &adaptor); +} + +static void +hb_font_paint_glyph_default (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + hb_paint_funcs_t *paint_funcs, + void *paint_data, + unsigned int palette, + hb_color_t foreground, + void *user_data) +{ + paint_funcs->push_transform (paint_data, + font->parent->x_scale ? (float) font->x_scale / (float) font->parent->x_scale : 0.f, + font->parent->y_scale ? (font->slant - font->parent->slant) * + (float) font->x_scale / (float) font->parent->y_scale : 0.f, + 0.f, + font->parent->y_scale ? (float) font->y_scale / (float) font->parent->y_scale : 0.f, + 0.f, 0.f); + + font->parent->paint_glyph (glyph, paint_funcs, paint_data, palette, foreground); + + paint_funcs->pop_transform (paint_data); +} + +DEFINE_NULL_INSTANCE (hb_font_funcs_t) = +{ + HB_OBJECT_HEADER_STATIC, + + nullptr, + nullptr, { { -#define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_nil, +#define HB_FONT_FUNC_IMPLEMENT(get_,name) hb_font_##get_##name##_nil, HB_FONT_FUNCS_IMPLEMENT_CALLBACKS #undef HB_FONT_FUNC_IMPLEMENT } } }; -static const hb_font_funcs_t _hb_font_funcs_parent = { + +static const hb_font_funcs_t _hb_font_funcs_default = { HB_OBJECT_HEADER_STATIC, - true, /* immutable */ - - { -#define HB_FONT_FUNC_IMPLEMENT(name) nullptr, - HB_FONT_FUNCS_IMPLEMENT_CALLBACKS -#undef HB_FONT_FUNC_IMPLEMENT - }, - { -#define HB_FONT_FUNC_IMPLEMENT(name) nullptr, - HB_FONT_FUNCS_IMPLEMENT_CALLBACKS -#undef HB_FONT_FUNC_IMPLEMENT - }, + nullptr, + nullptr, { { -#define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_parent, +#define HB_FONT_FUNC_IMPLEMENT(get_,name) hb_font_##get_##name##_default, HB_FONT_FUNCS_IMPLEMENT_CALLBACKS #undef HB_FONT_FUNC_IMPLEMENT } @@ -390,23 +710,23 @@ static const hb_font_funcs_t _hb_font_funcs_parent = { /** - * hb_font_funcs_create: (Xconstructor) + * hb_font_funcs_create: * - * + * Creates a new #hb_font_funcs_t structure of font functions. * - * Return value: (transfer full): + * Return value: (transfer full): The font-functions structure * * Since: 0.9.2 **/ hb_font_funcs_t * -hb_font_funcs_create (void) +hb_font_funcs_create () { hb_font_funcs_t *ffuncs; if (!(ffuncs = hb_object_create ())) return hb_font_funcs_get_empty (); - ffuncs->get = _hb_font_funcs_parent.get; + ffuncs->get = _hb_font_funcs_default.get; return ffuncs; } @@ -414,25 +734,25 @@ hb_font_funcs_create (void) /** * hb_font_funcs_get_empty: * - * + * Fetches an empty font-functions structure. * - * Return value: (transfer full): + * Return value: (transfer full): The font-functions structure * * Since: 0.9.2 **/ hb_font_funcs_t * -hb_font_funcs_get_empty (void) +hb_font_funcs_get_empty () { - return const_cast (&_hb_font_funcs_parent); + return const_cast (&_hb_font_funcs_default); } /** * hb_font_funcs_reference: (skip) - * @ffuncs: font functions. + * @ffuncs: The font-functions structure * - * + * Increases the reference count on a font-functions structure. * - * Return value: + * Return value: The font-functions structure * * Since: 0.9.2 **/ @@ -444,9 +764,11 @@ hb_font_funcs_reference (hb_font_funcs_t *ffuncs) /** * hb_font_funcs_destroy: (skip) - * @ffuncs: font functions. + * @ffuncs: The font-functions structure * - * + * Decreases the reference count on a font-functions structure. When + * the reference count reaches zero, the font-functions structure is + * destroyed, freeing all memory. * * Since: 0.9.2 **/ @@ -455,25 +777,31 @@ hb_font_funcs_destroy (hb_font_funcs_t *ffuncs) { if (!hb_object_destroy (ffuncs)) return; -#define HB_FONT_FUNC_IMPLEMENT(name) if (ffuncs->destroy.name) \ - ffuncs->destroy.name (ffuncs->user_data.name); - HB_FONT_FUNCS_IMPLEMENT_CALLBACKS + if (ffuncs->destroy) + { +#define HB_FONT_FUNC_IMPLEMENT(get_,name) if (ffuncs->destroy->name) \ + ffuncs->destroy->name (!ffuncs->user_data ? nullptr : ffuncs->user_data->name); + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS #undef HB_FONT_FUNC_IMPLEMENT + } - free (ffuncs); + hb_free (ffuncs->destroy); + hb_free (ffuncs->user_data); + + hb_free (ffuncs); } /** * hb_font_funcs_set_user_data: (skip) - * @ffuncs: font functions. - * @key: - * @data: - * @destroy: - * @replace: + * @ffuncs: The font-functions structure + * @key: The user-data key to set + * @data: A pointer to the user data set + * @destroy: (nullable): A callback to call when @data is not needed anymore + * @replace: Whether to replace an existing data with the same key * - * + * Attaches a user-data key/data pair to the specified font-functions structure. * - * Return value: + * Return value: `true` if success, `false` otherwise * * Since: 0.9.2 **/ @@ -481,7 +809,7 @@ hb_bool_t hb_font_funcs_set_user_data (hb_font_funcs_t *ffuncs, hb_user_data_key_t *key, void * data, - hb_destroy_func_t destroy, + hb_destroy_func_t destroy /* May be NULL. */, hb_bool_t replace) { return hb_object_set_user_data (ffuncs, key, data, destroy, replace); @@ -489,18 +817,19 @@ hb_font_funcs_set_user_data (hb_font_funcs_t *ffuncs, /** * hb_font_funcs_get_user_data: (skip) - * @ffuncs: font functions. - * @key: + * @ffuncs: The font-functions structure + * @key: The user-data key to query * - * + * Fetches the user data associated with the specified key, + * attached to the specified font-functions structure. * - * Return value: (transfer none): + * Return value: (transfer none): A pointer to the user data * * Since: 0.9.2 **/ void * -hb_font_funcs_get_user_data (hb_font_funcs_t *ffuncs, - hb_user_data_key_t *key) +hb_font_funcs_get_user_data (const hb_font_funcs_t *ffuncs, + hb_user_data_key_t *key) { return hb_object_get_user_data (ffuncs, key); } @@ -508,92 +837,148 @@ hb_font_funcs_get_user_data (hb_font_funcs_t *ffuncs, /** * hb_font_funcs_make_immutable: - * @ffuncs: font functions. + * @ffuncs: The font-functions structure * - * + * Makes a font-functions structure immutable. * * Since: 0.9.2 **/ void hb_font_funcs_make_immutable (hb_font_funcs_t *ffuncs) { - if (unlikely (hb_object_is_inert (ffuncs))) + if (hb_object_is_immutable (ffuncs)) return; - ffuncs->immutable = true; + hb_object_make_immutable (ffuncs); } /** * hb_font_funcs_is_immutable: - * @ffuncs: font functions. + * @ffuncs: The font-functions structure * - * + * Tests whether a font-functions structure is immutable. * - * Return value: + * Return value: `true` if @ffuncs is immutable, `false` otherwise * * Since: 0.9.2 **/ hb_bool_t hb_font_funcs_is_immutable (hb_font_funcs_t *ffuncs) { - return ffuncs->immutable; + return hb_object_is_immutable (ffuncs); } -#define HB_FONT_FUNC_IMPLEMENT(name) \ - \ +static bool +_hb_font_funcs_set_preamble (hb_font_funcs_t *ffuncs, + bool func_is_null, + void **user_data, + hb_destroy_func_t *destroy) +{ + if (hb_object_is_immutable (ffuncs)) + { + if (*destroy) + (*destroy) (*user_data); + return false; + } + + if (func_is_null) + { + if (*destroy) + (*destroy) (*user_data); + *destroy = nullptr; + *user_data = nullptr; + } + + return true; +} + +static bool +_hb_font_funcs_set_middle (hb_font_funcs_t *ffuncs, + void *user_data, + hb_destroy_func_t destroy) +{ + if (user_data && !ffuncs->user_data) + { + ffuncs->user_data = (decltype (ffuncs->user_data)) hb_calloc (1, sizeof (*ffuncs->user_data)); + if (unlikely (!ffuncs->user_data)) + goto fail; + } + if (destroy && !ffuncs->destroy) + { + ffuncs->destroy = (decltype (ffuncs->destroy)) hb_calloc (1, sizeof (*ffuncs->destroy)); + if (unlikely (!ffuncs->destroy)) + goto fail; + } + + return true; + +fail: + if (destroy) + (destroy) (user_data); + return false; +} + +#define HB_FONT_FUNC_IMPLEMENT(get_,name) \ + \ void \ hb_font_funcs_set_##name##_func (hb_font_funcs_t *ffuncs, \ - hb_font_get_##name##_func_t func, \ - void *user_data, \ - hb_destroy_func_t destroy) \ + hb_font_##get_##name##_func_t func, \ + void *user_data, \ + hb_destroy_func_t destroy) \ { \ - if (ffuncs->immutable) { \ - if (destroy) \ - destroy (user_data); \ - return; \ - } \ + if (!_hb_font_funcs_set_preamble (ffuncs, !func, &user_data, &destroy))\ + return; \ + \ + if (ffuncs->destroy && ffuncs->destroy->name) \ + ffuncs->destroy->name (!ffuncs->user_data ? nullptr : ffuncs->user_data->name); \ \ - if (ffuncs->destroy.name) \ - ffuncs->destroy.name (ffuncs->user_data.name); \ - \ - if (func) { \ + if (!_hb_font_funcs_set_middle (ffuncs, user_data, destroy)) \ + return; \ + \ + if (func) \ ffuncs->get.f.name = func; \ - ffuncs->user_data.name = user_data; \ - ffuncs->destroy.name = destroy; \ - } else { \ - ffuncs->get.f.name = hb_font_get_##name##_parent; \ - ffuncs->user_data.name = nullptr; \ - ffuncs->destroy.name = nullptr; \ - } \ + else \ + ffuncs->get.f.name = hb_font_##get_##name##_default; \ + \ + if (ffuncs->user_data) \ + ffuncs->user_data->name = user_data; \ + if (ffuncs->destroy) \ + ffuncs->destroy->name = destroy; \ } HB_FONT_FUNCS_IMPLEMENT_CALLBACKS #undef HB_FONT_FUNC_IMPLEMENT +bool +hb_font_t::has_func_set (unsigned int i) +{ + return this->klass->get.array[i] != _hb_font_funcs_default.get.array[i]; +} + bool hb_font_t::has_func (unsigned int i) { - if (parent && parent != hb_font_get_empty () && parent->has_func (i)) - return true; - return this->klass->get.array[i] != _hb_font_funcs_parent.get.array[i]; + return has_func_set (i) || + (parent && parent != &_hb_Null_hb_font_t && parent->has_func (i)); } /* Public getters */ /** * hb_font_get_h_extents: - * @font: a font. - * @extents: (out): + * @font: #hb_font_t to work upon + * @extents: (out): The font extents retrieved * + * Fetches the extents for a specified font, for horizontal + * text segments. * - * - * Return value: + * Return value: `true` if data found, `false` otherwise * * Since: 1.1.3 **/ hb_bool_t -hb_font_get_h_extents (hb_font_t *font, +hb_font_get_h_extents (hb_font_t *font, hb_font_extents_t *extents) { return font->get_font_h_extents (extents); @@ -601,17 +986,18 @@ hb_font_get_h_extents (hb_font_t *font, /** * hb_font_get_v_extents: - * @font: a font. - * @extents: (out): + * @font: #hb_font_t to work upon + * @extents: (out): The font extents retrieved * + * Fetches the extents for a specified font, for vertical + * text segments. * - * - * Return value: + * Return value: `true` if data found, `false` otherwise * * Since: 1.1.3 **/ hb_bool_t -hb_font_get_v_extents (hb_font_t *font, +hb_font_get_v_extents (hb_font_t *font, hb_font_extents_t *extents) { return font->get_font_v_extents (extents); @@ -619,20 +1005,25 @@ hb_font_get_v_extents (hb_font_t *font, /** * hb_font_get_glyph: - * @font: a font. - * @unicode: - * @variation_selector: - * @glyph: (out): + * @font: #hb_font_t to work upon + * @unicode: The Unicode code point to query + * @variation_selector: A variation-selector code point + * @glyph: (out): The glyph ID retrieved * - * + * Fetches the glyph ID for a Unicode code point in the specified + * font, with an optional variation selector. * - * Return value: + * If @variation_selector is 0, calls hb_font_get_nominal_glyph(); + * otherwise calls hb_font_get_variation_glyph(). + * + * Return value: `true` if data found, `false` otherwise * * Since: 0.9.2 **/ hb_bool_t -hb_font_get_glyph (hb_font_t *font, - hb_codepoint_t unicode, hb_codepoint_t variation_selector, +hb_font_get_glyph (hb_font_t *font, + hb_codepoint_t unicode, + hb_codepoint_t variation_selector, hb_codepoint_t *glyph) { if (unlikely (variation_selector)) @@ -642,40 +1033,78 @@ hb_font_get_glyph (hb_font_t *font, /** * hb_font_get_nominal_glyph: - * @font: a font. - * @unicode: - * @glyph: (out): + * @font: #hb_font_t to work upon + * @unicode: The Unicode code point to query + * @glyph: (out): The glyph ID retrieved * - * + * Fetches the nominal glyph ID for a Unicode code point in the + * specified font. * - * Return value: + * This version of the function should not be used to fetch glyph IDs + * for code points modified by variation selectors. For variation-selector + * support, user hb_font_get_variation_glyph() or use hb_font_get_glyph(). + * + * Return value: `true` if data found, `false` otherwise * * Since: 1.2.3 **/ hb_bool_t -hb_font_get_nominal_glyph (hb_font_t *font, - hb_codepoint_t unicode, +hb_font_get_nominal_glyph (hb_font_t *font, + hb_codepoint_t unicode, hb_codepoint_t *glyph) { return font->get_nominal_glyph (unicode, glyph); } +/** + * hb_font_get_nominal_glyphs: + * @font: #hb_font_t to work upon + * @count: number of code points to query + * @first_unicode: The first Unicode code point to query + * @unicode_stride: The stride between successive code points + * @first_glyph: (out): The first glyph ID retrieved + * @glyph_stride: The stride between successive glyph IDs + * + * Fetches the nominal glyph IDs for a sequence of Unicode code points. Glyph + * IDs must be returned in a #hb_codepoint_t output parameter. Stops at the + * first unsupported glyph ID. + * + * Return value: the number of code points processed + * + * Since: 2.6.3 + **/ +unsigned int +hb_font_get_nominal_glyphs (hb_font_t *font, + unsigned int count, + const hb_codepoint_t *first_unicode, + unsigned int unicode_stride, + hb_codepoint_t *first_glyph, + unsigned int glyph_stride) +{ + return font->get_nominal_glyphs (count, + first_unicode, unicode_stride, + first_glyph, glyph_stride); +} + /** * hb_font_get_variation_glyph: - * @font: a font. - * @unicode: - * @variation_selector: - * @glyph: (out): + * @font: #hb_font_t to work upon + * @unicode: The Unicode code point to query + * @variation_selector: The variation-selector code point to query + * @glyph: (out): The glyph ID retrieved * - * + * Fetches the glyph ID for a Unicode code point when followed by + * by the specified variation-selector code point, in the specified + * font. * - * Return value: + * Return value: `true` if data found, `false` otherwise * * Since: 1.2.3 **/ hb_bool_t -hb_font_get_variation_glyph (hb_font_t *font, - hb_codepoint_t unicode, hb_codepoint_t variation_selector, +hb_font_get_variation_glyph (hb_font_t *font, + hb_codepoint_t unicode, + hb_codepoint_t variation_selector, hb_codepoint_t *glyph) { return font->get_variation_glyph (unicode, variation_selector, glyph); @@ -683,135 +1112,204 @@ hb_font_get_variation_glyph (hb_font_t *font, /** * hb_font_get_glyph_h_advance: - * @font: a font. - * @glyph: + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query * - * + * Fetches the advance for a glyph ID in the specified font, + * for horizontal text segments. * - * Return value: + * Return value: The advance of @glyph within @font * * Since: 0.9.2 **/ hb_position_t -hb_font_get_glyph_h_advance (hb_font_t *font, - hb_codepoint_t glyph) +hb_font_get_glyph_h_advance (hb_font_t *font, + hb_codepoint_t glyph) { return font->get_glyph_h_advance (glyph); } /** * hb_font_get_glyph_v_advance: - * @font: a font. - * @glyph: + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query * - * + * Fetches the advance for a glyph ID in the specified font, + * for vertical text segments. * - * Return value: + * Return value: The advance of @glyph within @font * * Since: 0.9.2 **/ hb_position_t -hb_font_get_glyph_v_advance (hb_font_t *font, - hb_codepoint_t glyph) +hb_font_get_glyph_v_advance (hb_font_t *font, + hb_codepoint_t glyph) { return font->get_glyph_v_advance (glyph); } +/** + * hb_font_get_glyph_h_advances: + * @font: #hb_font_t to work upon + * @count: The number of glyph IDs in the sequence queried + * @first_glyph: The first glyph ID to query + * @glyph_stride: The stride between successive glyph IDs + * @first_advance: (out): The first advance retrieved + * @advance_stride: The stride between successive advances + * + * Fetches the advances for a sequence of glyph IDs in the specified + * font, for horizontal text segments. + * + * Since: 1.8.6 + **/ +void +hb_font_get_glyph_h_advances (hb_font_t* font, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride) +{ + font->get_glyph_h_advances (count, first_glyph, glyph_stride, first_advance, advance_stride); +} +/** + * hb_font_get_glyph_v_advances: + * @font: #hb_font_t to work upon + * @count: The number of glyph IDs in the sequence queried + * @first_glyph: The first glyph ID to query + * @glyph_stride: The stride between successive glyph IDs + * @first_advance: (out): The first advance retrieved + * @advance_stride: (out): The stride between successive advances + * + * Fetches the advances for a sequence of glyph IDs in the specified + * font, for vertical text segments. + * + * Since: 1.8.6 + **/ +void +hb_font_get_glyph_v_advances (hb_font_t* font, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride) +{ + font->get_glyph_v_advances (count, first_glyph, glyph_stride, first_advance, advance_stride); +} + /** * hb_font_get_glyph_h_origin: - * @font: a font. - * @glyph: - * @x: (out): - * @y: (out): + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query + * @x: (out): The X coordinate of the origin + * @y: (out): The Y coordinate of the origin * - * + * Fetches the (X,Y) coordinates of the origin for a glyph ID + * in the specified font, for horizontal text segments. * - * Return value: + * Return value: `true` if data found, `false` otherwise * * Since: 0.9.2 **/ hb_bool_t -hb_font_get_glyph_h_origin (hb_font_t *font, - hb_codepoint_t glyph, - hb_position_t *x, hb_position_t *y) +hb_font_get_glyph_h_origin (hb_font_t *font, + hb_codepoint_t glyph, + hb_position_t *x, + hb_position_t *y) { return font->get_glyph_h_origin (glyph, x, y); } /** * hb_font_get_glyph_v_origin: - * @font: a font. - * @glyph: - * @x: (out): - * @y: (out): + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query + * @x: (out): The X coordinate of the origin + * @y: (out): The Y coordinate of the origin * - * + * Fetches the (X,Y) coordinates of the origin for a glyph ID + * in the specified font, for vertical text segments. * - * Return value: + * Return value: `true` if data found, `false` otherwise * * Since: 0.9.2 **/ hb_bool_t -hb_font_get_glyph_v_origin (hb_font_t *font, - hb_codepoint_t glyph, - hb_position_t *x, hb_position_t *y) +hb_font_get_glyph_v_origin (hb_font_t *font, + hb_codepoint_t glyph, + hb_position_t *x, + hb_position_t *y) { return font->get_glyph_v_origin (glyph, x, y); } /** * hb_font_get_glyph_h_kerning: - * @font: a font. - * @left_glyph: - * @right_glyph: + * @font: #hb_font_t to work upon + * @left_glyph: The glyph ID of the left glyph in the glyph pair + * @right_glyph: The glyph ID of the right glyph in the glyph pair * - * + * Fetches the kerning-adjustment value for a glyph-pair in + * the specified font, for horizontal text segments. * - * Return value: + * It handles legacy kerning only (as returned by the corresponding + * #hb_font_funcs_t function). + * + * Return value: The kerning adjustment value * * Since: 0.9.2 **/ hb_position_t -hb_font_get_glyph_h_kerning (hb_font_t *font, - hb_codepoint_t left_glyph, hb_codepoint_t right_glyph) +hb_font_get_glyph_h_kerning (hb_font_t *font, + hb_codepoint_t left_glyph, + hb_codepoint_t right_glyph) { return font->get_glyph_h_kerning (left_glyph, right_glyph); } +#ifndef HB_DISABLE_DEPRECATED /** * hb_font_get_glyph_v_kerning: - * @font: a font. - * @top_glyph: - * @bottom_glyph: + * @font: #hb_font_t to work upon + * @top_glyph: The glyph ID of the top glyph in the glyph pair + * @bottom_glyph: The glyph ID of the bottom glyph in the glyph pair * - * + * Fetches the kerning-adjustment value for a glyph-pair in + * the specified font, for vertical text segments. * - * Return value: + * It handles legacy kerning only (as returned by the corresponding + * #hb_font_funcs_t function). + * + * Return value: The kerning adjustment value * * Since: 0.9.2 + * Deprecated: 2.0.0 **/ hb_position_t -hb_font_get_glyph_v_kerning (hb_font_t *font, - hb_codepoint_t top_glyph, hb_codepoint_t bottom_glyph) +hb_font_get_glyph_v_kerning (hb_font_t *font, + hb_codepoint_t top_glyph, + hb_codepoint_t bottom_glyph) { return font->get_glyph_v_kerning (top_glyph, bottom_glyph); } +#endif /** * hb_font_get_glyph_extents: - * @font: a font. - * @glyph: - * @extents: (out): + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query + * @extents: (out): The #hb_glyph_extents_t retrieved * - * + * Fetches the #hb_glyph_extents_t data for a glyph ID + * in the specified font. * - * Return value: + * Return value: `true` if data found, `false` otherwise * * Since: 0.9.2 **/ hb_bool_t -hb_font_get_glyph_extents (hb_font_t *font, - hb_codepoint_t glyph, +hb_font_get_glyph_extents (hb_font_t *font, + hb_codepoint_t glyph, hb_glyph_extents_t *extents) { return font->get_glyph_extents (glyph, extents); @@ -819,211 +1317,364 @@ hb_font_get_glyph_extents (hb_font_t *font, /** * hb_font_get_glyph_contour_point: - * @font: a font. - * @glyph: - * @point_index: - * @x: (out): - * @y: (out): + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query + * @point_index: The contour-point index to query + * @x: (out): The X value retrieved for the contour point + * @y: (out): The Y value retrieved for the contour point * - * + * Fetches the (x,y) coordinates of a specified contour-point index + * in the specified glyph, within the specified font. * - * Return value: + * Return value: `true` if data found, `false` otherwise * * Since: 0.9.2 **/ hb_bool_t -hb_font_get_glyph_contour_point (hb_font_t *font, - hb_codepoint_t glyph, unsigned int point_index, - hb_position_t *x, hb_position_t *y) +hb_font_get_glyph_contour_point (hb_font_t *font, + hb_codepoint_t glyph, + unsigned int point_index, + hb_position_t *x, + hb_position_t *y) { return font->get_glyph_contour_point (glyph, point_index, x, y); } /** * hb_font_get_glyph_name: - * @font: a font. - * @glyph: - * @name: (array length=size): - * @size: + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query + * @name: (out) (array length=size): Name string retrieved for the glyph ID + * @size: Length of the glyph-name string retrieved * - * + * Fetches the glyph-name string for a glyph ID in the specified @font. * - * Return value: + * According to the OpenType specification, glyph names are limited to 63 + * characters and can only contain (a subset of) ASCII. + * + * Return value: `true` if data found, `false` otherwise * * Since: 0.9.2 **/ hb_bool_t -hb_font_get_glyph_name (hb_font_t *font, - hb_codepoint_t glyph, - char *name, unsigned int size) +hb_font_get_glyph_name (hb_font_t *font, + hb_codepoint_t glyph, + char *name, + unsigned int size) { return font->get_glyph_name (glyph, name, size); } /** * hb_font_get_glyph_from_name: - * @font: a font. - * @name: (array length=len): - * @len: - * @glyph: (out): + * @font: #hb_font_t to work upon + * @name: (array length=len): The name string to query + * @len: The length of the name queried + * @glyph: (out): The glyph ID retrieved * - * + * Fetches the glyph ID that corresponds to a name string in the specified @font. * - * Return value: + * Note: @len == -1 means the name string is null-terminated. + * + * Return value: `true` if data found, `false` otherwise * * Since: 0.9.2 **/ hb_bool_t -hb_font_get_glyph_from_name (hb_font_t *font, - const char *name, int len, /* -1 means nul-terminated */ +hb_font_get_glyph_from_name (hb_font_t *font, + const char *name, + int len, /* -1 means nul-terminated */ hb_codepoint_t *glyph) { return font->get_glyph_from_name (name, len, glyph); } +#ifndef HB_DISABLE_DEPRECATED +/** + * hb_font_get_glyph_shape: + * @font: #hb_font_t to work upon + * @glyph: The glyph ID + * @dfuncs: #hb_draw_funcs_t to draw to + * @draw_data: User data to pass to draw callbacks + * + * Fetches the glyph shape that corresponds to a glyph in the specified @font. + * The shape is returned by way of calls to the callbacks of the @dfuncs + * objects, with @draw_data passed to them. + * + * Since: 4.0.0 + * Deprecated: 7.0.0: Use hb_font_draw_glyph() instead + */ +void +hb_font_get_glyph_shape (hb_font_t *font, + hb_codepoint_t glyph, + hb_draw_funcs_t *dfuncs, void *draw_data) +{ + hb_font_draw_glyph (font, glyph, dfuncs, draw_data); +} +#endif + +/** + * hb_font_draw_glyph: + * @font: #hb_font_t to work upon + * @glyph: The glyph ID + * @dfuncs: #hb_draw_funcs_t to draw to + * @draw_data: User data to pass to draw callbacks + * + * Draws the outline that corresponds to a glyph in the specified @font. + * + * The outline is returned by way of calls to the callbacks of the @dfuncs + * objects, with @draw_data passed to them. + * + * Since: 7.0.0 + **/ +void +hb_font_draw_glyph (hb_font_t *font, + hb_codepoint_t glyph, + hb_draw_funcs_t *dfuncs, void *draw_data) +{ + font->draw_glyph (glyph, dfuncs, draw_data); +} + +/** + * hb_font_paint_glyph: + * @font: #hb_font_t to work upon + * @glyph: The glyph ID + * @pfuncs: #hb_paint_funcs_t to paint with + * @paint_data: User data to pass to paint callbacks + * @palette_index: The index of the font's color palette to use + * @foreground: The foreground color, unpremultipled + * + * Paints the glyph. + * + * The painting instructions are returned by way of calls to + * the callbacks of the @funcs object, with @paint_data passed + * to them. + * + * If the font has color palettes (see hb_ot_color_has_palettes()), + * then @palette_index selects the palette to use. If the font only + * has one palette, this will be 0. + * + * Since: 7.0.0 + */ +void +hb_font_paint_glyph (hb_font_t *font, + hb_codepoint_t glyph, + hb_paint_funcs_t *pfuncs, void *paint_data, + unsigned int palette_index, + hb_color_t foreground) +{ + font->paint_glyph (glyph, pfuncs, paint_data, palette_index, foreground); +} /* A bit higher-level, and with fallback */ /** * hb_font_get_extents_for_direction: - * @font: a font. - * @direction: - * @extents: + * @font: #hb_font_t to work upon + * @direction: The direction of the text segment + * @extents: (out): The #hb_font_extents_t retrieved * + * Fetches the extents for a font in a text segment of the + * specified direction. * + * Calls the appropriate direction-specific variant (horizontal + * or vertical) depending on the value of @direction. * * Since: 1.1.3 **/ void -hb_font_get_extents_for_direction (hb_font_t *font, - hb_direction_t direction, +hb_font_get_extents_for_direction (hb_font_t *font, + hb_direction_t direction, hb_font_extents_t *extents) { - return font->get_extents_for_direction (direction, extents); + font->get_extents_for_direction (direction, extents); } /** * hb_font_get_glyph_advance_for_direction: - * @font: a font. - * @glyph: - * @direction: - * @x: (out): - * @y: (out): + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query + * @direction: The direction of the text segment + * @x: (out): The horizontal advance retrieved + * @y: (out): The vertical advance retrieved * - * + * Fetches the advance for a glyph ID from the specified font, + * in a text segment of the specified direction. + * + * Calls the appropriate direction-specific variant (horizontal + * or vertical) depending on the value of @direction. * * Since: 0.9.2 **/ void -hb_font_get_glyph_advance_for_direction (hb_font_t *font, - hb_codepoint_t glyph, - hb_direction_t direction, - hb_position_t *x, hb_position_t *y) +hb_font_get_glyph_advance_for_direction (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, + hb_position_t *y) { - return font->get_glyph_advance_for_direction (glyph, direction, x, y); + font->get_glyph_advance_for_direction (glyph, direction, x, y); +} +/** + * hb_font_get_glyph_advances_for_direction: + * @font: #hb_font_t to work upon + * @direction: The direction of the text segment + * @count: The number of glyph IDs in the sequence queried + * @first_glyph: The first glyph ID to query + * @glyph_stride: The stride between successive glyph IDs + * @first_advance: (out): The first advance retrieved + * @advance_stride: (out): The stride between successive advances + * + * Fetches the advances for a sequence of glyph IDs in the specified + * font, in a text segment of the specified direction. + * + * Calls the appropriate direction-specific variant (horizontal + * or vertical) depending on the value of @direction. + * + * Since: 1.8.6 + **/ +HB_EXTERN void +hb_font_get_glyph_advances_for_direction (hb_font_t* font, + hb_direction_t direction, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride) +{ + font->get_glyph_advances_for_direction (direction, count, first_glyph, glyph_stride, first_advance, advance_stride); } /** * hb_font_get_glyph_origin_for_direction: - * @font: a font. - * @glyph: - * @direction: - * @x: (out): - * @y: (out): + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query + * @direction: The direction of the text segment + * @x: (out): The X coordinate retrieved for the origin + * @y: (out): The Y coordinate retrieved for the origin * - * + * Fetches the (X,Y) coordinates of the origin for a glyph in + * the specified font. + * + * Calls the appropriate direction-specific variant (horizontal + * or vertical) depending on the value of @direction. * * Since: 0.9.2 **/ void -hb_font_get_glyph_origin_for_direction (hb_font_t *font, - hb_codepoint_t glyph, - hb_direction_t direction, - hb_position_t *x, hb_position_t *y) +hb_font_get_glyph_origin_for_direction (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, + hb_position_t *y) { return font->get_glyph_origin_for_direction (glyph, direction, x, y); } /** * hb_font_add_glyph_origin_for_direction: - * @font: a font. - * @glyph: - * @direction: - * @x: (out): - * @y: (out): + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query + * @direction: The direction of the text segment + * @x: (inout): Input = The original X coordinate + * Output = The X coordinate plus the X-coordinate of the origin + * @y: (inout): Input = The original Y coordinate + * Output = The Y coordinate plus the Y-coordinate of the origin * - * + * Adds the origin coordinates to an (X,Y) point coordinate, in + * the specified glyph ID in the specified font. + * + * Calls the appropriate direction-specific variant (horizontal + * or vertical) depending on the value of @direction. * * Since: 0.9.2 **/ void -hb_font_add_glyph_origin_for_direction (hb_font_t *font, - hb_codepoint_t glyph, - hb_direction_t direction, - hb_position_t *x, hb_position_t *y) +hb_font_add_glyph_origin_for_direction (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, + hb_position_t *y) { return font->add_glyph_origin_for_direction (glyph, direction, x, y); } /** * hb_font_subtract_glyph_origin_for_direction: - * @font: a font. - * @glyph: - * @direction: - * @x: (out): - * @y: (out): + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query + * @direction: The direction of the text segment + * @x: (inout): Input = The original X coordinate + * Output = The X coordinate minus the X-coordinate of the origin + * @y: (inout): Input = The original Y coordinate + * Output = The Y coordinate minus the Y-coordinate of the origin * - * + * Subtracts the origin coordinates from an (X,Y) point coordinate, + * in the specified glyph ID in the specified font. + * + * Calls the appropriate direction-specific variant (horizontal + * or vertical) depending on the value of @direction. * * Since: 0.9.2 **/ void -hb_font_subtract_glyph_origin_for_direction (hb_font_t *font, - hb_codepoint_t glyph, - hb_direction_t direction, - hb_position_t *x, hb_position_t *y) +hb_font_subtract_glyph_origin_for_direction (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, + hb_position_t *y) { return font->subtract_glyph_origin_for_direction (glyph, direction, x, y); } /** * hb_font_get_glyph_kerning_for_direction: - * @font: a font. - * @first_glyph: - * @second_glyph: - * @direction: - * @x: (out): - * @y: (out): + * @font: #hb_font_t to work upon + * @first_glyph: The glyph ID of the first glyph in the glyph pair to query + * @second_glyph: The glyph ID of the second glyph in the glyph pair to query + * @direction: The direction of the text segment + * @x: (out): The horizontal kerning-adjustment value retrieved + * @y: (out): The vertical kerning-adjustment value retrieved * - * + * Fetches the kerning-adjustment value for a glyph-pair in the specified font. + * + * Calls the appropriate direction-specific variant (horizontal + * or vertical) depending on the value of @direction. * * Since: 0.9.2 **/ void -hb_font_get_glyph_kerning_for_direction (hb_font_t *font, - hb_codepoint_t first_glyph, hb_codepoint_t second_glyph, - hb_direction_t direction, - hb_position_t *x, hb_position_t *y) +hb_font_get_glyph_kerning_for_direction (hb_font_t *font, + hb_codepoint_t first_glyph, + hb_codepoint_t second_glyph, + hb_direction_t direction, + hb_position_t *x, + hb_position_t *y) { return font->get_glyph_kerning_for_direction (first_glyph, second_glyph, direction, x, y); } /** * hb_font_get_glyph_extents_for_origin: - * @font: a font. - * @glyph: - * @direction: - * @extents: (out): + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query + * @direction: The direction of the text segment + * @extents: (out): The #hb_glyph_extents_t retrieved * - * + * Fetches the #hb_glyph_extents_t data for a glyph ID + * in the specified font, with respect to the origin in + * a text segment in the specified direction. * - * Return value: + * Calls the appropriate direction-specific variant (horizontal + * or vertical) depending on the value of @direction. + * + * Return value: `true` if data found, `false` otherwise * * Since: 0.9.2 **/ hb_bool_t -hb_font_get_glyph_extents_for_origin (hb_font_t *font, - hb_codepoint_t glyph, - hb_direction_t direction, +hb_font_get_glyph_extents_for_origin (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, hb_glyph_extents_t *extents) { return font->get_glyph_extents_for_origin (glyph, direction, extents); @@ -1031,65 +1682,82 @@ hb_font_get_glyph_extents_for_origin (hb_font_t *font, /** * hb_font_get_glyph_contour_point_for_origin: - * @font: a font. - * @glyph: - * @point_index: - * @direction: - * @x: (out): - * @y: (out): + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query + * @point_index: The contour-point index to query + * @direction: The direction of the text segment + * @x: (out): The X value retrieved for the contour point + * @y: (out): The Y value retrieved for the contour point * - * + * Fetches the (X,Y) coordinates of a specified contour-point index + * in the specified glyph ID in the specified font, with respect + * to the origin in a text segment in the specified direction. * - * Return value: + * Calls the appropriate direction-specific variant (horizontal + * or vertical) depending on the value of @direction. + * + * Return value: `true` if data found, `false` otherwise * * Since: 0.9.2 **/ hb_bool_t -hb_font_get_glyph_contour_point_for_origin (hb_font_t *font, - hb_codepoint_t glyph, unsigned int point_index, - hb_direction_t direction, - hb_position_t *x, hb_position_t *y) +hb_font_get_glyph_contour_point_for_origin (hb_font_t *font, + hb_codepoint_t glyph, + unsigned int point_index, + hb_direction_t direction, + hb_position_t *x, + hb_position_t *y) { return font->get_glyph_contour_point_for_origin (glyph, point_index, direction, x, y); } -/* Generates gidDDD if glyph has no name. */ /** * hb_font_glyph_to_string: - * @font: a font. - * @glyph: - * @s: (array length=size): - * @size: + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query + * @s: (out) (array length=size): The string containing the glyph name + * @size: Length of string @s * - * + * Fetches the name of the specified glyph ID in @font and returns + * it in string @s. + * + * If the glyph ID has no name in @font, a string of the form `gidDDD` is + * generated, with `DDD` being the glyph ID. + * + * According to the OpenType specification, glyph names are limited to 63 + * characters and can only contain (a subset of) ASCII. * * Since: 0.9.2 **/ void -hb_font_glyph_to_string (hb_font_t *font, - hb_codepoint_t glyph, - char *s, unsigned int size) +hb_font_glyph_to_string (hb_font_t *font, + hb_codepoint_t glyph, + char *s, + unsigned int size) { font->glyph_to_string (glyph, s, size); } -/* Parses gidDDD and uniUUUU strings automatically. */ /** * hb_font_glyph_from_string: - * @font: a font. - * @s: (array length=len) (element-type uint8_t): - * @len: - * @glyph: (out): + * @font: #hb_font_t to work upon + * @s: (array length=len) (element-type uint8_t): string to query + * @len: The length of the string @s + * @glyph: (out): The glyph ID corresponding to the string requested * - * + * Fetches the glyph ID from @font that matches the specified string. + * Strings of the format `gidDDD` or `uniUUUU` are parsed automatically. * - * Return value: + * Note: @len == -1 means the string is null-terminated. + * + * Return value: `true` if data found, `false` otherwise * * Since: 0.9.2 **/ hb_bool_t -hb_font_glyph_from_string (hb_font_t *font, - const char *s, int len, /* -1 means nul-terminated */ +hb_font_glyph_from_string (hb_font_t *font, + const char *s, + int len, hb_codepoint_t *glyph) { return font->glyph_from_string (s, len, glyph); @@ -1100,23 +1768,53 @@ hb_font_glyph_from_string (hb_font_t *font, * hb_font_t */ -/** - * hb_font_create: (Xconstructor) - * @face: a face. - * - * - * - * Return value: (transfer full): - * - * Since: 0.9.2 - **/ -hb_font_t * -hb_font_create (hb_face_t *face) +DEFINE_NULL_INSTANCE (hb_font_t) = +{ + HB_OBJECT_HEADER_STATIC, + + 0, /* serial */ + 0, /* serial_coords */ + + nullptr, /* parent */ + const_cast (&_hb_Null_hb_face_t), + + 1000, /* x_scale */ + 1000, /* y_scale */ + 0.f, /* x_embolden */ + 0.f, /* y_embolden */ + true, /* embolden_in_place */ + 0, /* x_strength */ + 0, /* y_strength */ + 0.f, /* slant */ + 0.f, /* slant_xy; */ + 1.f, /* x_multf */ + 1.f, /* y_multf */ + 1<<16, /* x_mult */ + 1<<16, /* y_mult */ + + 0, /* x_ppem */ + 0, /* y_ppem */ + 0, /* ptem */ + + HB_FONT_NO_VAR_NAMED_INSTANCE, /* instance_index */ + 0, /* num_coords */ + nullptr, /* coords */ + nullptr, /* design_coords */ + + const_cast (&_hb_Null_hb_font_funcs_t), + + /* Zero for the rest is fine. */ +}; + + +static hb_font_t * +_hb_font_create (hb_face_t *face) { hb_font_t *font; if (unlikely (!face)) face = hb_face_get_empty (); + if (!(font = hb_object_create ())) return hb_font_get_empty (); @@ -1124,19 +1822,75 @@ hb_font_create (hb_face_t *face) font->parent = hb_font_get_empty (); font->face = hb_face_reference (face); font->klass = hb_font_funcs_get_empty (); - - font->x_scale = font->y_scale = hb_face_get_upem (face); + font->data.init0 (font); + font->x_scale = font->y_scale = face->get_upem (); + font->embolden_in_place = true; + font->x_multf = font->y_multf = 1.f; + font->x_mult = font->y_mult = 1 << 16; + font->instance_index = HB_FONT_NO_VAR_NAMED_INSTANCE; return font; } +/** + * hb_font_create: + * @face: a face. + * + * Constructs a new font object from the specified face. + * + * Note: If @face's index value (as passed to hb_face_create() + * has non-zero top 16-bits, those bits minus one are passed to + * hb_font_set_var_named_instance(), effectively loading a named-instance + * of a variable font, instead of the default-instance. This allows + * specifying which named-instance to load by default when creating the + * face. + * + * Return value: (transfer full): The new font object + * + * Since: 0.9.2 + **/ +hb_font_t * +hb_font_create (hb_face_t *face) +{ + hb_font_t *font = _hb_font_create (face); + +#ifndef HB_NO_OT_FONT + /* Install our in-house, very lightweight, funcs. */ + hb_ot_font_set_funcs (font); +#endif + +#ifndef HB_NO_VAR + if (face && face->index >> 16) + hb_font_set_var_named_instance (font, (face->index >> 16) - 1); +#endif + + return font; +} + +static void +_hb_font_adopt_var_coords (hb_font_t *font, + int *coords, /* 2.14 normalized */ + float *design_coords, + unsigned int coords_length) +{ + hb_free (font->coords); + hb_free (font->design_coords); + + font->coords = coords; + font->design_coords = design_coords; + font->num_coords = coords_length; + + font->mults_changed (); // Easiest to call this to drop cached data +} + /** * hb_font_create_sub_font: - * @parent: parent font. + * @parent: The parent font object * - * + * Constructs a sub-font font object from the specified @parent font, + * replicating the parent's properties. * - * Return value: (transfer full): + * Return value: (transfer full): The new sub-font font object * * Since: 0.9.2 **/ @@ -1146,86 +1900,68 @@ hb_font_create_sub_font (hb_font_t *parent) if (unlikely (!parent)) parent = hb_font_get_empty (); - hb_font_t *font = hb_font_create (parent->face); + hb_font_t *font = _hb_font_create (parent->face); - if (unlikely (hb_object_is_inert (font))) + if (unlikely (hb_object_is_immutable (font))) return font; font->parent = hb_font_reference (parent); font->x_scale = parent->x_scale; font->y_scale = parent->y_scale; + font->x_embolden = parent->x_embolden; + font->y_embolden = parent->y_embolden; + font->embolden_in_place = parent->embolden_in_place; + font->slant = parent->slant; font->x_ppem = parent->x_ppem; font->y_ppem = parent->y_ppem; font->ptem = parent->ptem; - font->num_coords = parent->num_coords; - if (!font->num_coords) - font->coords = nullptr; - else + unsigned int num_coords = parent->num_coords; + if (num_coords) { - unsigned int size = parent->num_coords * sizeof (parent->coords[0]); - font->coords = (int *) malloc (size); - if (unlikely (!font->coords)) - font->num_coords = 0; + int *coords = (int *) hb_calloc (num_coords, sizeof (parent->coords[0])); + float *design_coords = (float *) hb_calloc (num_coords, sizeof (parent->design_coords[0])); + if (likely (coords && design_coords)) + { + hb_memcpy (coords, parent->coords, num_coords * sizeof (parent->coords[0])); + hb_memcpy (design_coords, parent->design_coords, num_coords * sizeof (parent->design_coords[0])); + _hb_font_adopt_var_coords (font, coords, design_coords, num_coords); + } else - memcpy (font->coords, parent->coords, size); + { + hb_free (coords); + hb_free (design_coords); + } } + font->mults_changed (); + return font; } /** * hb_font_get_empty: * - * + * Fetches the empty font object. * - * Return value: (transfer full) + * Return value: (transfer full): The empty font object * * Since: 0.9.2 **/ hb_font_t * -hb_font_get_empty (void) +hb_font_get_empty () { - static const hb_font_t _hb_font_nil = { - HB_OBJECT_HEADER_STATIC, - - true, /* immutable */ - - nullptr, /* parent */ - const_cast (&_hb_face_nil), - - 1000, /* x_scale */ - 1000, /* y_scale */ - - 0, /* x_ppem */ - 0, /* y_ppem */ - 0, /* ptem */ - - 0, /* num_coords */ - nullptr, /* coords */ - - const_cast (&_hb_font_funcs_nil), /* klass */ - nullptr, /* user_data */ - nullptr, /* destroy */ - - { -#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID, -#include "hb-shaper-list.hh" -#undef HB_SHAPER_IMPLEMENT - } - }; - - return const_cast (&_hb_font_nil); + return const_cast (&Null (hb_font_t)); } /** * hb_font_reference: (skip) - * @font: a font. + * @font: #hb_font_t to work upon * - * + * Increases the reference count on the given font object. * - * Return value: (transfer full): + * Return value: (transfer full): The @font object * * Since: 0.9.2 **/ @@ -1237,9 +1973,11 @@ hb_font_reference (hb_font_t *font) /** * hb_font_destroy: (skip) - * @font: a font. + * @font: #hb_font_t to work upon * - * + * Decreases the reference count on the given font object. When the + * reference count reaches zero, the font is destroyed, + * freeing all memory. * * Since: 0.9.2 **/ @@ -1248,9 +1986,7 @@ hb_font_destroy (hb_font_t *font) { if (!hb_object_destroy (font)) return; -#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_DESTROY(shaper, font); -#include "hb-shaper-list.hh" -#undef HB_SHAPER_IMPLEMENT + font->data.fini (); if (font->destroy) font->destroy (font->user_data); @@ -1259,22 +1995,23 @@ hb_font_destroy (hb_font_t *font) hb_face_destroy (font->face); hb_font_funcs_destroy (font->klass); - free (font->coords); + hb_free (font->coords); + hb_free (font->design_coords); - free (font); + hb_free (font); } /** * hb_font_set_user_data: (skip) - * @font: a font. - * @key: - * @data: - * @destroy: - * @replace: + * @font: #hb_font_t to work upon + * @key: The user-data key + * @data: A pointer to the user data + * @destroy: (nullable): A callback to call when @data is not needed anymore + * @replace: Whether to replace an existing data with the same key * - * + * Attaches a user-data key/data pair to the specified font object. * - * Return value: + * Return value: `true` if success, `false` otherwise * * Since: 0.9.2 **/ @@ -1282,25 +2019,29 @@ hb_bool_t hb_font_set_user_data (hb_font_t *font, hb_user_data_key_t *key, void * data, - hb_destroy_func_t destroy, + hb_destroy_func_t destroy /* May be NULL. */, hb_bool_t replace) { + if (!hb_object_is_immutable (font)) + font->serial++; + return hb_object_set_user_data (font, key, data, destroy, replace); } /** * hb_font_get_user_data: (skip) - * @font: a font. - * @key: + * @font: #hb_font_t to work upon + * @key: The user-data key to query * - * + * Fetches the user-data object associated with the specified key, + * attached to the specified font object. * - * Return value: (transfer none): + * Return value: (transfer none): Pointer to the user data * * Since: 0.9.2 **/ void * -hb_font_get_user_data (hb_font_t *font, +hb_font_get_user_data (const hb_font_t *font, hb_user_data_key_t *key) { return hb_object_get_user_data (font, key); @@ -1308,46 +2049,85 @@ hb_font_get_user_data (hb_font_t *font, /** * hb_font_make_immutable: - * @font: a font. + * @font: #hb_font_t to work upon * - * + * Makes @font immutable. * * Since: 0.9.2 **/ void hb_font_make_immutable (hb_font_t *font) { - if (unlikely (hb_object_is_inert (font))) + if (hb_object_is_immutable (font)) return; if (font->parent) hb_font_make_immutable (font->parent); - font->immutable = true; + hb_object_make_immutable (font); } /** * hb_font_is_immutable: - * @font: a font. + * @font: #hb_font_t to work upon * - * + * Tests whether a font object is immutable. * - * Return value: + * Return value: `true` if @font is immutable, `false` otherwise * * Since: 0.9.2 **/ hb_bool_t hb_font_is_immutable (hb_font_t *font) { - return font->immutable; + return hb_object_is_immutable (font); +} + +/** + * hb_font_get_serial: + * @font: #hb_font_t to work upon + * + * Returns the internal serial number of the font. The serial + * number is increased every time a setting on the font is + * changed, using a setter function. + * + * Return value: serial number + * + * Since: 4.4.0 + **/ +unsigned int +hb_font_get_serial (hb_font_t *font) +{ + return font->serial; +} + +/** + * hb_font_changed: + * @font: #hb_font_t to work upon + * + * Notifies the @font that underlying font data has changed. + * This has the effect of increasing the serial as returned + * by hb_font_get_serial(), which invalidates internal caches. + * + * Since: 4.4.0 + **/ +void +hb_font_changed (hb_font_t *font) +{ + if (hb_object_is_immutable (font)) + return; + + font->serial++; + + font->mults_changed (); } /** * hb_font_set_parent: - * @font: a font. - * @parent: new parent. + * @font: #hb_font_t to work upon + * @parent: The parent font object to assign * - * Sets parent font of @font. + * Sets the parent font of @font. * * Since: 1.0.5 **/ @@ -1355,9 +2135,14 @@ void hb_font_set_parent (hb_font_t *font, hb_font_t *parent) { - if (font->immutable) + if (hb_object_is_immutable (font)) return; + if (parent == font->parent) + return; + + font->serial++; + if (!parent) parent = hb_font_get_empty (); @@ -1370,11 +2155,11 @@ hb_font_set_parent (hb_font_t *font, /** * hb_font_get_parent: - * @font: a font. + * @font: #hb_font_t to work upon * - * + * Fetches the parent font of @font. * - * Return value: (transfer none): + * Return value: (transfer none): The parent font object * * Since: 0.9.2 **/ @@ -1386,10 +2171,10 @@ hb_font_get_parent (hb_font_t *font) /** * hb_font_set_face: - * @font: a font. - * @face: new face. + * @font: #hb_font_t to work upon + * @face: The #hb_face_t to assign * - * Sets font-face of @font. + * Sets @face as the font-face value of @font. * * Since: 1.4.3 **/ @@ -1397,26 +2182,33 @@ void hb_font_set_face (hb_font_t *font, hb_face_t *face) { - if (font->immutable) + if (hb_object_is_immutable (font)) return; + if (face == font->face) + return; + + font->serial++; + if (unlikely (!face)) face = hb_face_get_empty (); hb_face_t *old = font->face; + hb_face_make_immutable (face); font->face = hb_face_reference (face); + font->mults_changed (); hb_face_destroy (old); } /** * hb_font_get_face: - * @font: a font. + * @font: #hb_font_t to work upon * - * + * Fetches the face associated with the specified font object. * - * Return value: (transfer none): + * Return value: (transfer none): The #hb_face_t value * * Since: 0.9.2 **/ @@ -1429,12 +2221,13 @@ hb_font_get_face (hb_font_t *font) /** * hb_font_set_funcs: - * @font: a font. - * @klass: (closure font_data) (destroy destroy) (scope notified): - * @font_data: - * @destroy: + * @font: #hb_font_t to work upon + * @klass: (closure font_data) (destroy destroy) (scope notified): The font-functions structure. + * @font_data: Data to attach to @font + * @destroy: (nullable): The function to call when @font_data is not needed anymore * - * + * Replaces the font-functions structure attached to a font, updating + * the font's user-data with @font-data and the @destroy callback. * * Since: 0.9.2 **/ @@ -1442,14 +2235,17 @@ void hb_font_set_funcs (hb_font_t *font, hb_font_funcs_t *klass, void *font_data, - hb_destroy_func_t destroy) + hb_destroy_func_t destroy /* May be NULL. */) { - if (font->immutable) { + if (hb_object_is_immutable (font)) + { if (destroy) destroy (font_data); return; } + font->serial++; + if (font->destroy) font->destroy (font->user_data); @@ -1465,26 +2261,30 @@ hb_font_set_funcs (hb_font_t *font, /** * hb_font_set_funcs_data: - * @font: a font. - * @font_data: (destroy destroy) (scope notified): - * @destroy: + * @font: #hb_font_t to work upon + * @font_data: (destroy destroy) (scope notified): Data to attach to @font + * @destroy: (nullable): The function to call when @font_data is not needed anymore * - * + * Replaces the user data attached to a font, updating the font's + * @destroy callback. * * Since: 0.9.2 **/ void hb_font_set_funcs_data (hb_font_t *font, void *font_data, - hb_destroy_func_t destroy) + hb_destroy_func_t destroy /* May be NULL. */) { /* Destroy user_data? */ - if (font->immutable) { + if (hb_object_is_immutable (font)) + { if (destroy) destroy (font_data); return; } + font->serial++; + if (font->destroy) font->destroy (font->user_data); @@ -1495,40 +2295,71 @@ hb_font_set_funcs_data (hb_font_t *font, /** * hb_font_set_scale: - * @font: a font. - * @x_scale: - * @y_scale: + * @font: #hb_font_t to work upon + * @x_scale: Horizontal scale value to assign + * @y_scale: Vertical scale value to assign * - * + * Sets the horizontal and vertical scale of a font. + * + * The font scale is a number related to, but not the same as, + * font size. Typically the client establishes a scale factor + * to be used between the two. For example, 64, or 256, which + * would be the fractional-precision part of the font scale. + * This is necessary because #hb_position_t values are integer + * types and you need to leave room for fractional values + * in there. + * + * For example, to set the font size to 20, with 64 + * levels of fractional precision you would call + * `hb_font_set_scale(font, 20 * 64, 20 * 64)`. + * + * In the example above, even what font size 20 means is up to + * you. It might be 20 pixels, or 20 points, or 20 millimeters. + * HarfBuzz does not care about that. You can set the point + * size of the font using hb_font_set_ptem(), and the pixel + * size using hb_font_set_ppem(). + * + * The choice of scale is yours but needs to be consistent between + * what you set here, and what you expect out of #hb_position_t + * as well has draw / paint API output values. + * + * Fonts default to a scale equal to the UPEM value of their face. + * A font with this setting is sometimes called an "unscaled" font. * * Since: 0.9.2 **/ void hb_font_set_scale (hb_font_t *font, - int x_scale, - int y_scale) + int x_scale, + int y_scale) { - if (font->immutable) + if (hb_object_is_immutable (font)) return; + if (font->x_scale == x_scale && font->y_scale == y_scale) + return; + + font->serial++; + font->x_scale = x_scale; font->y_scale = y_scale; + font->mults_changed (); } /** * hb_font_get_scale: - * @font: a font. - * @x_scale: (out): - * @y_scale: (out): + * @font: #hb_font_t to work upon + * @x_scale: (out): Horizontal scale value + * @y_scale: (out): Vertical scale value * - * + * Fetches the horizontal and vertical scale of a font. * * Since: 0.9.2 **/ void hb_font_get_scale (hb_font_t *font, - int *x_scale, - int *y_scale) + int *x_scale, + int *y_scale) { if (x_scale) *x_scale = font->x_scale; if (y_scale) *y_scale = font->y_scale; @@ -1536,38 +2367,47 @@ hb_font_get_scale (hb_font_t *font, /** * hb_font_set_ppem: - * @font: a font. - * @x_ppem: - * @y_ppem: + * @font: #hb_font_t to work upon + * @x_ppem: Horizontal ppem value to assign + * @y_ppem: Vertical ppem value to assign * - * + * Sets the horizontal and vertical pixels-per-em (PPEM) of a font. + * + * These values are used for pixel-size-specific adjustment to + * shaping and draw results, though for the most part they are + * unused and can be left unset. * * Since: 0.9.2 **/ void -hb_font_set_ppem (hb_font_t *font, - unsigned int x_ppem, - unsigned int y_ppem) +hb_font_set_ppem (hb_font_t *font, + unsigned int x_ppem, + unsigned int y_ppem) { - if (font->immutable) + if (hb_object_is_immutable (font)) return; + if (font->x_ppem == x_ppem && font->y_ppem == y_ppem) + return; + + font->serial++; + font->x_ppem = x_ppem; font->y_ppem = y_ppem; } /** * hb_font_get_ppem: - * @font: a font. - * @x_ppem: (out): - * @y_ppem: (out): + * @font: #hb_font_t to work upon + * @x_ppem: (out): Horizontal ppem value + * @y_ppem: (out): Vertical ppem value * - * + * Fetches the horizontal and vertical points-per-em (ppem) of a font. * * Since: 0.9.2 **/ void -hb_font_get_ppem (hb_font_t *font, +hb_font_get_ppem (hb_font_t *font, unsigned int *x_ppem, unsigned int *y_ppem) { @@ -1577,31 +2417,41 @@ hb_font_get_ppem (hb_font_t *font, /** * hb_font_set_ptem: - * @font: a font. - * @ptem: + * @font: #hb_font_t to work upon + * @ptem: font size in points. * - * Sets "point size" of the font. + * Sets the "point size" of a font. Set to zero to unset. + * Used in CoreText to implement optical sizing. + * + * Note: There are 72 points in an inch. * * Since: 1.6.0 **/ void -hb_font_set_ptem (hb_font_t *font, float ptem) +hb_font_set_ptem (hb_font_t *font, + float ptem) { - if (font->immutable) + if (hb_object_is_immutable (font)) return; + if (font->ptem == ptem) + return; + + font->serial++; + font->ptem = ptem; } /** * hb_font_get_ptem: - * @font: a font. + * @font: #hb_font_t to work upon * - * Gets the "point size" of the font. A value of 0 means unset. + * Fetches the "point size" of a font. Used in CoreText to + * implement optical sizing. * - * Return value: Point size. + * Return value: Point size. A value of zero means "not set." * - * Since: 0.9.2 + * Since: 1.6.0 **/ float hb_font_get_ptem (hb_font_t *font) @@ -1609,106 +2459,430 @@ hb_font_get_ptem (hb_font_t *font) return font->ptem; } +/** + * hb_font_set_synthetic_bold: + * @font: #hb_font_t to work upon + * @x_embolden: the amount to embolden horizontally + * @y_embolden: the amount to embolden vertically + * @in_place: whether to embolden glyphs in-place + * + * Sets the "synthetic boldness" of a font. + * + * Positive values for @x_embolden / @y_embolden make a font + * bolder, negative values thinner. Typical values are in the + * 0.01 to 0.05 range. The default value is zero. + * + * Synthetic boldness is applied by offsetting the contour + * points of the glyph shape. + * + * Synthetic boldness is applied when rendering a glyph via + * hb_font_draw_glyph(). + * + * If @in_place is `false`, then glyph advance-widths are also + * adjusted, otherwise they are not. The in-place mode is + * useful for simulating [font grading](https://fonts.google.com/knowledge/glossary/grade). + * + * + * Since: 7.0.0 + **/ +void +hb_font_set_synthetic_bold (hb_font_t *font, + float x_embolden, + float y_embolden, + hb_bool_t in_place) +{ + if (hb_object_is_immutable (font)) + return; + + if (font->x_embolden == x_embolden && + font->y_embolden == y_embolden && + font->embolden_in_place == (bool) in_place) + return; + + font->serial++; + + font->x_embolden = x_embolden; + font->y_embolden = y_embolden; + font->embolden_in_place = in_place; + font->mults_changed (); +} + +/** + * hb_font_get_synthetic_bold: + * @font: #hb_font_t to work upon + * @x_embolden: (out): return location for horizontal value + * @y_embolden: (out): return location for vertical value + * @in_place: (out): return location for in-place value + * + * Fetches the "synthetic boldness" parameters of a font. + * + * Since: 7.0.0 + **/ +void +hb_font_get_synthetic_bold (hb_font_t *font, + float *x_embolden, + float *y_embolden, + hb_bool_t *in_place) +{ + if (x_embolden) *x_embolden = font->x_embolden; + if (y_embolden) *y_embolden = font->y_embolden; + if (in_place) *in_place = font->embolden_in_place; +} + +/** + * hb_font_set_synthetic_slant: + * @font: #hb_font_t to work upon + * @slant: synthetic slant value. + * + * Sets the "synthetic slant" of a font. By default is zero. + * Synthetic slant is the graphical skew applied to the font + * at rendering time. + * + * HarfBuzz needs to know this value to adjust shaping results, + * metrics, and style values to match the slanted rendering. + * + * Note: The glyph shape fetched via the hb_font_draw_glyph() + * function is slanted to reflect this value as well. + * + * Note: The slant value is a ratio. For example, a + * 20% slant would be represented as a 0.2 value. + * + * Since: 3.3.0 + **/ +HB_EXTERN void +hb_font_set_synthetic_slant (hb_font_t *font, float slant) +{ + if (hb_object_is_immutable (font)) + return; + + if (font->slant == slant) + return; + + font->serial++; + + font->slant = slant; + font->mults_changed (); +} + +/** + * hb_font_get_synthetic_slant: + * @font: #hb_font_t to work upon + * + * Fetches the "synthetic slant" of a font. + * + * Return value: Synthetic slant. By default is zero. + * + * Since: 3.3.0 + **/ +HB_EXTERN float +hb_font_get_synthetic_slant (hb_font_t *font) +{ + return font->slant; +} + +#ifndef HB_NO_VAR /* * Variations */ -static void -_hb_font_adopt_var_coords_normalized (hb_font_t *font, - int *coords, /* 2.14 normalized */ - unsigned int coords_length) -{ - free (font->coords); - - font->coords = coords; - font->num_coords = coords_length; -} - /** * hb_font_set_variations: + * @font: #hb_font_t to work upon + * @variations: (array length=variations_length): Array of variation settings to apply + * @variations_length: Number of variations to apply + * + * Applies a list of font-variation settings to a font. + * + * Note that this overrides all existing variations set on @font. + * Axes not included in @variations will be effectively set to their + * default values. * * Since: 1.4.2 */ void -hb_font_set_variations (hb_font_t *font, +hb_font_set_variations (hb_font_t *font, const hb_variation_t *variations, - unsigned int variations_length) + unsigned int variations_length) { - if (font->immutable) + if (hb_object_is_immutable (font)) return; - if (!variations_length) + font->serial_coords = ++font->serial; + + if (!variations_length && font->instance_index == HB_FONT_NO_VAR_NAMED_INSTANCE) { hb_font_set_var_coords_normalized (font, nullptr, 0); return; } - unsigned int coords_length = hb_ot_var_get_axis_count (font->face); + const OT::fvar &fvar = *font->face->table.fvar; + auto axes = fvar.get_axes (); + const unsigned coords_length = axes.length; - int *normalized = coords_length ? (int *) calloc (coords_length, sizeof (int)) : nullptr; - if (unlikely (coords_length && !normalized)) + int *normalized = coords_length ? (int *) hb_calloc (coords_length, sizeof (int)) : nullptr; + float *design_coords = coords_length ? (float *) hb_calloc (coords_length, sizeof (float)) : nullptr; + + if (unlikely (coords_length && !(normalized && design_coords))) + { + hb_free (normalized); + hb_free (design_coords); + return; + } + + /* Initialize design coords. */ + for (unsigned int i = 0; i < coords_length; i++) + design_coords[i] = axes[i].get_default (); + if (font->instance_index != HB_FONT_NO_VAR_NAMED_INSTANCE) + { + unsigned count = coords_length; + /* This may fail if index is out-of-range; + * That's why we initialize design_coords from fvar above + * unconditionally. */ + hb_ot_var_named_instance_get_design_coords (font->face, font->instance_index, + &count, design_coords); + } + + for (unsigned int i = 0; i < variations_length; i++) + { + const auto tag = variations[i].tag; + const auto v = variations[i].value; + for (unsigned axis_index = 0; axis_index < coords_length; axis_index++) + if (axes[axis_index].axisTag == tag) + design_coords[axis_index] = v; + } + + hb_ot_var_normalize_coords (font->face, coords_length, design_coords, normalized); + _hb_font_adopt_var_coords (font, normalized, design_coords, coords_length); +} + +/** + * hb_font_set_variation: + * @font: #hb_font_t to work upon + * @tag: The #hb_tag_t tag of the variation-axis name + * @value: The value of the variation axis + * + * Change the value of one variation axis on the font. + * + * Note: This function is expensive to be called repeatedly. + * If you want to set multiple variation axes at the same time, + * use hb_font_set_variations() instead. + * + * Since: 7.1.0 + */ +void +hb_font_set_variation (hb_font_t *font, + hb_tag_t tag, + float value) +{ + if (hb_object_is_immutable (font)) return; - hb_ot_var_normalize_variations (font->face, - variations, variations_length, - normalized, coords_length); - _hb_font_adopt_var_coords_normalized (font, normalized, coords_length); + font->serial_coords = ++font->serial; + + // TODO Share some of this code with set_variations() + + const OT::fvar &fvar = *font->face->table.fvar; + auto axes = fvar.get_axes (); + const unsigned coords_length = axes.length; + + int *normalized = coords_length ? (int *) hb_calloc (coords_length, sizeof (int)) : nullptr; + float *design_coords = coords_length ? (float *) hb_calloc (coords_length, sizeof (float)) : nullptr; + + if (unlikely (coords_length && !(normalized && design_coords))) + { + hb_free (normalized); + hb_free (design_coords); + return; + } + + /* Initialize design coords. */ + if (font->design_coords) + { + assert (coords_length == font->num_coords); + for (unsigned int i = 0; i < coords_length; i++) + design_coords[i] = font->design_coords[i]; + } + else + { + for (unsigned int i = 0; i < coords_length; i++) + design_coords[i] = axes[i].get_default (); + if (font->instance_index != HB_FONT_NO_VAR_NAMED_INSTANCE) + { + unsigned count = coords_length; + /* This may fail if index is out-of-range; + * That's why we initialize design_coords from fvar above + * unconditionally. */ + hb_ot_var_named_instance_get_design_coords (font->face, font->instance_index, + &count, design_coords); + } + } + + for (unsigned axis_index = 0; axis_index < coords_length; axis_index++) + if (axes[axis_index].axisTag == tag) + design_coords[axis_index] = value; + + hb_ot_var_normalize_coords (font->face, coords_length, design_coords, normalized); + _hb_font_adopt_var_coords (font, normalized, design_coords, coords_length); + } /** * hb_font_set_var_coords_design: + * @font: #hb_font_t to work upon + * @coords: (array length=coords_length): Array of variation coordinates to apply + * @coords_length: Number of coordinates to apply + * + * Applies a list of variation coordinates (in design-space units) + * to a font. + * + * Note that this overrides all existing variations set on @font. + * Axes not included in @coords will be effectively set to their + * default values. * * Since: 1.4.2 */ void -hb_font_set_var_coords_design (hb_font_t *font, - const float *coords, - unsigned int coords_length) +hb_font_set_var_coords_design (hb_font_t *font, + const float *coords, + unsigned int coords_length) { - if (font->immutable) + if (hb_object_is_immutable (font)) return; - int *normalized = coords_length ? (int *) calloc (coords_length, sizeof (int)) : nullptr; - if (unlikely (coords_length && !normalized)) + font->serial_coords = ++font->serial; + + int *normalized = coords_length ? (int *) hb_calloc (coords_length, sizeof (int)) : nullptr; + float *design_coords = coords_length ? (float *) hb_calloc (coords_length, sizeof (float)) : nullptr; + + if (unlikely (coords_length && !(normalized && design_coords))) + { + hb_free (normalized); + hb_free (design_coords); return; + } + + if (coords_length) + hb_memcpy (design_coords, coords, coords_length * sizeof (font->design_coords[0])); hb_ot_var_normalize_coords (font->face, coords_length, coords, normalized); - _hb_font_adopt_var_coords_normalized (font, normalized, coords_length); + _hb_font_adopt_var_coords (font, normalized, design_coords, coords_length); +} + +/** + * hb_font_set_var_named_instance: + * @font: a font. + * @instance_index: named instance index. + * + * Sets design coords of a font from a named-instance index. + * + * Since: 2.6.0 + */ +void +hb_font_set_var_named_instance (hb_font_t *font, + unsigned int instance_index) +{ + if (hb_object_is_immutable (font)) + return; + + if (font->instance_index == instance_index) + return; + + font->serial_coords = ++font->serial; + + font->instance_index = instance_index; + hb_font_set_variations (font, nullptr, 0); +} + +/** + * hb_font_get_var_named_instance: + * @font: a font. + * + * Returns the currently-set named-instance index of the font. + * + * Return value: Named-instance index or %HB_FONT_NO_VAR_NAMED_INSTANCE. + * + * Since: 7.0.0 + **/ +unsigned int +hb_font_get_var_named_instance (hb_font_t *font) +{ + return font->instance_index; } /** * hb_font_set_var_coords_normalized: + * @font: #hb_font_t to work upon + * @coords: (array length=coords_length): Array of variation coordinates to apply + * @coords_length: Number of coordinates to apply + * + * Applies a list of variation coordinates (in normalized units) + * to a font. + * + * Note that this overrides all existing variations set on @font. + * Axes not included in @coords will be effectively set to their + * default values. + * + * Note: Coordinates should be normalized to 2.14. * * Since: 1.4.2 */ void -hb_font_set_var_coords_normalized (hb_font_t *font, - const int *coords, /* 2.14 normalized */ - unsigned int coords_length) +hb_font_set_var_coords_normalized (hb_font_t *font, + const int *coords, /* 2.14 normalized */ + unsigned int coords_length) { - if (font->immutable) + if (hb_object_is_immutable (font)) return; - int *copy = coords_length ? (int *) calloc (coords_length, sizeof (coords[0])) : nullptr; - if (unlikely (coords_length && !copy)) + font->serial_coords = ++font->serial; + + int *copy = coords_length ? (int *) hb_calloc (coords_length, sizeof (coords[0])) : nullptr; + int *unmapped = coords_length ? (int *) hb_calloc (coords_length, sizeof (coords[0])) : nullptr; + float *design_coords = coords_length ? (float *) hb_calloc (coords_length, sizeof (design_coords[0])) : nullptr; + + if (unlikely (coords_length && !(copy && unmapped && design_coords))) + { + hb_free (copy); + hb_free (unmapped); + hb_free (design_coords); return; + } if (coords_length) - memcpy (copy, coords, coords_length * sizeof (coords[0])); + { + hb_memcpy (copy, coords, coords_length * sizeof (coords[0])); + hb_memcpy (unmapped, coords, coords_length * sizeof (coords[0])); + } - _hb_font_adopt_var_coords_normalized (font, copy, coords_length); + /* Best effort design coords simulation */ + font->face->table.avar->unmap_coords (unmapped, coords_length); + for (unsigned int i = 0; i < coords_length; ++i) + design_coords[i] = font->face->table.fvar->unnormalize_axis_value (i, unmapped[i]); + hb_free (unmapped); + + _hb_font_adopt_var_coords (font, copy, design_coords, coords_length); } /** * hb_font_get_var_coords_normalized: + * @font: #hb_font_t to work upon + * @length: (out): Number of coordinates retrieved + * + * Fetches the list of normalized variation coordinates currently + * set on a font. + * + * Note that this returned array may only contain values for some + * (or none) of the axes; omitted axes effectively have zero values. * * Return value is valid as long as variation coordinates of the font * are not modified. * + * Return value: coordinates array + * * Since: 1.4.2 */ const int * -hb_font_get_var_coords_normalized (hb_font_t *font, +hb_font_get_var_coords_normalized (hb_font_t *font, unsigned int *length) { if (length) @@ -1717,9 +2891,37 @@ hb_font_get_var_coords_normalized (hb_font_t *font, return font->coords; } +/** + * hb_font_get_var_coords_design: + * @font: #hb_font_t to work upon + * @length: (out): Number of coordinates retrieved + * + * Fetches the list of variation coordinates (in design-space units) currently + * set on a font. + * + * Note that this returned array may only contain values for some + * (or none) of the axes; omitted axes effectively have their default + * values. + * + * Return value is valid as long as variation coordinates of the font + * are not modified. + * + * Return value: coordinates array + * + * Since: 3.3.0 + */ +const float * +hb_font_get_var_coords_design (hb_font_t *font, + unsigned int *length) +{ + if (length) + *length = font->num_coords; + + return font->design_coords; +} +#endif #ifndef HB_DISABLE_DEPRECATED - /* * Deprecated get_glyph_func(): */ @@ -1746,7 +2948,7 @@ trampoline_create (FuncType func, { typedef hb_trampoline_t trampoline_t; - trampoline_t *trampoline = (trampoline_t *) calloc (1, sizeof (trampoline_t)); + trampoline_t *trampoline = (trampoline_t *) hb_calloc (1, sizeof (trampoline_t)); if (unlikely (!trampoline)) return nullptr; @@ -1775,29 +2977,29 @@ trampoline_destroy (void *user_data) if (closure->destroy) closure->destroy (closure->user_data); - free (closure); + hb_free (closure); } typedef hb_trampoline_t hb_font_get_glyph_trampoline_t; static hb_bool_t -hb_font_get_nominal_glyph_trampoline (hb_font_t *font, - void *font_data, - hb_codepoint_t unicode, +hb_font_get_nominal_glyph_trampoline (hb_font_t *font, + void *font_data, + hb_codepoint_t unicode, hb_codepoint_t *glyph, - void *user_data) + void *user_data) { hb_font_get_glyph_trampoline_t *trampoline = (hb_font_get_glyph_trampoline_t *) user_data; return trampoline->func (font, font_data, unicode, 0, glyph, trampoline->closure.user_data); } static hb_bool_t -hb_font_get_variation_glyph_trampoline (hb_font_t *font, - void *font_data, - hb_codepoint_t unicode, - hb_codepoint_t variation_selector, +hb_font_get_variation_glyph_trampoline (hb_font_t *font, + void *font_data, + hb_codepoint_t unicode, + hb_codepoint_t variation_selector, hb_codepoint_t *glyph, - void *user_data) + void *user_data) { hb_font_get_glyph_trampoline_t *trampoline = (hb_font_get_glyph_trampoline_t *) user_data; return trampoline->func (font, font_data, unicode, variation_selector, glyph, trampoline->closure.user_data); @@ -1805,10 +3007,10 @@ 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: + * @ffuncs: The font-functions structure + * @func: (closure user_data) (destroy destroy) (scope notified): callback function + * @user_data: data to pass to @func + * @destroy: (nullable): 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. @@ -1817,10 +3019,18 @@ hb_font_get_variation_glyph_trampoline (hb_font_t *font, * Deprecated: 1.2.3 **/ void -hb_font_funcs_set_glyph_func (hb_font_funcs_t *ffuncs, - hb_font_get_glyph_func_t func, - void *user_data, hb_destroy_func_t destroy) +hb_font_funcs_set_glyph_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_func_t func, + void *user_data, + hb_destroy_func_t destroy /* May be NULL. */) { + if (hb_object_is_immutable (ffuncs)) + { + if (destroy) + destroy (user_data); + return; + } + hb_font_get_glyph_trampoline_t *trampoline; trampoline = trampoline_create (func, user_data, destroy); @@ -1831,16 +3041,29 @@ hb_font_funcs_set_glyph_func (hb_font_funcs_t *ffuncs, return; } + /* Since we pass it to two destroying functions. */ + trampoline_reference (&trampoline->closure); + hb_font_funcs_set_nominal_glyph_func (ffuncs, hb_font_get_nominal_glyph_trampoline, trampoline, trampoline_destroy); - trampoline_reference (&trampoline->closure); hb_font_funcs_set_variation_glyph_func (ffuncs, hb_font_get_variation_glyph_trampoline, trampoline, trampoline_destroy); } +#endif -#endif /* HB_DISABLE_DEPRECATED */ + +#ifndef HB_DISABLE_DEPRECATED +void +hb_font_funcs_set_glyph_shape_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_shape_func_t func, + void *user_data, + hb_destroy_func_t destroy /* May be NULL. */) +{ + hb_font_funcs_set_draw_glyph_func (ffuncs, func, user_data, destroy); +} +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-font.h b/src/3rdparty/harfbuzz-ng/src/hb-font.h index 540cdcab953..3c2355af2d7 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-font.h +++ b/src/3rdparty/harfbuzz-ng/src/hb-font.h @@ -24,7 +24,7 @@ * Red Hat Author(s): Behdad Esfahbod */ -#ifndef HB_H_IN +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) #error "Include instead." #endif @@ -33,17 +33,28 @@ #include "hb-common.h" #include "hb-face.h" +#include "hb-draw.h" +#include "hb-paint.h" HB_BEGIN_DECLS - -typedef struct hb_font_t hb_font_t; - - /* * hb_font_funcs_t */ +/** + * hb_font_funcs_t: + * + * Data type containing a set of virtual methods used for + * working on #hb_font_t font objects. + * + * HarfBuzz provides a lightweight default function for each of + * the methods in #hb_font_funcs_t. Client programs can implement + * their own replacements for the individual font functions, as + * needed, and replace the default by calling the setter for a + * method. + * + **/ typedef struct hb_font_funcs_t hb_font_funcs_t; HB_EXTERN hb_font_funcs_t * @@ -67,8 +78,8 @@ hb_font_funcs_set_user_data (hb_font_funcs_t *ffuncs, HB_EXTERN void * -hb_font_funcs_get_user_data (hb_font_funcs_t *ffuncs, - hb_user_data_key_t *key); +hb_font_funcs_get_user_data (const hb_font_funcs_t *ffuncs, + hb_user_data_key_t *key); HB_EXTERN void @@ -78,14 +89,23 @@ HB_EXTERN hb_bool_t hb_font_funcs_is_immutable (hb_font_funcs_t *ffuncs); -/* font and glyph extents */ +/* font extents */ -/* Note that typically ascender is positive and descender negative in coordinate systems that grow up. */ -typedef struct hb_font_extents_t -{ - hb_position_t ascender; /* typographic ascender. */ - hb_position_t descender; /* typographic descender. */ - hb_position_t line_gap; /* suggested line spacing gap. */ +/** + * hb_font_extents_t: + * @ascender: The height of typographic ascenders. + * @descender: The depth of typographic descenders. + * @line_gap: The suggested line-spacing gap. + * + * Font-wide extent values, measured in font units. + * + * Note that typically @ascender is positive and @descender + * negative, in coordinate systems that grow up. + **/ +typedef struct hb_font_extents_t { + hb_position_t ascender; + hb_position_t descender; + hb_position_t line_gap; /*< private >*/ hb_position_t reserved9; hb_position_t reserved8; @@ -98,84 +118,424 @@ typedef struct hb_font_extents_t hb_position_t reserved1; } hb_font_extents_t; -/* Note that height is negative in coordinate systems that grow up. */ -typedef struct hb_glyph_extents_t -{ - hb_position_t x_bearing; /* left side of glyph from origin. */ - hb_position_t y_bearing; /* top side of glyph from origin. */ - hb_position_t width; /* distance from left to right side. */ - hb_position_t height; /* distance from top to bottom side. */ -} hb_glyph_extents_t; - /* func types */ +/** + * hb_font_get_font_extents_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @extents: (out): The font extents retrieved + * @user_data: User data pointer passed by the caller + * + * This method should retrieve the extents for a font. + * + **/ typedef hb_bool_t (*hb_font_get_font_extents_func_t) (hb_font_t *font, void *font_data, - hb_font_extents_t *metrics, + hb_font_extents_t *extents, void *user_data); + +/** + * hb_font_get_font_h_extents_func_t: + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the extents for a font, for horizontal-direction + * text segments. Extents must be returned in an #hb_glyph_extents output + * parameter. + * + **/ typedef hb_font_get_font_extents_func_t hb_font_get_font_h_extents_func_t; + +/** + * hb_font_get_font_v_extents_func_t: + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the extents for a font, for vertical-direction + * text segments. Extents must be returned in an #hb_glyph_extents output + * parameter. + * + **/ typedef hb_font_get_font_extents_func_t hb_font_get_font_v_extents_func_t; +/** + * hb_font_get_nominal_glyph_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @unicode: The Unicode code point to query + * @glyph: (out): The glyph ID retrieved + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the nominal glyph ID for a specified Unicode code + * point. Glyph IDs must be returned in a #hb_codepoint_t output parameter. + * + * Return value: `true` if data found, `false` otherwise + * + **/ typedef hb_bool_t (*hb_font_get_nominal_glyph_func_t) (hb_font_t *font, void *font_data, hb_codepoint_t unicode, hb_codepoint_t *glyph, void *user_data); + +/** + * hb_font_get_variation_glyph_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @unicode: The Unicode code point to query + * @variation_selector: The variation-selector code point to query + * @glyph: (out): The glyph ID retrieved + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the glyph ID for a specified Unicode code point + * followed by a specified Variation Selector code point. Glyph IDs must be + * returned in a #hb_codepoint_t output parameter. + * + * Return value: `true` if data found, `false` otherwise + * + **/ typedef hb_bool_t (*hb_font_get_variation_glyph_func_t) (hb_font_t *font, void *font_data, hb_codepoint_t unicode, hb_codepoint_t variation_selector, hb_codepoint_t *glyph, void *user_data); +/** + * hb_font_get_nominal_glyphs_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @count: number of code points to query + * @first_unicode: The first Unicode code point to query + * @unicode_stride: The stride between successive code points + * @first_glyph: (out): The first glyph ID retrieved + * @glyph_stride: The stride between successive glyph IDs + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the nominal glyph IDs for a sequence of + * Unicode code points. Glyph IDs must be returned in a #hb_codepoint_t + * output parameter. + * + * Return value: the number of code points processed + * + **/ +typedef unsigned int (*hb_font_get_nominal_glyphs_func_t) (hb_font_t *font, void *font_data, + unsigned int count, + const hb_codepoint_t *first_unicode, + unsigned int unicode_stride, + hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + void *user_data); + +/** + * hb_font_get_glyph_advance_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @glyph: The glyph ID to query + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the advance for a specified glyph. The + * method must return an #hb_position_t. + * + * Return value: The advance of @glyph within @font + * + **/ typedef hb_position_t (*hb_font_get_glyph_advance_func_t) (hb_font_t *font, void *font_data, hb_codepoint_t glyph, void *user_data); + +/** + * hb_font_get_glyph_h_advance_func_t: + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the advance for a specified glyph, in + * horizontal-direction text segments. Advances must be returned in + * an #hb_position_t output parameter. + * + **/ typedef hb_font_get_glyph_advance_func_t hb_font_get_glyph_h_advance_func_t; + +/** + * hb_font_get_glyph_v_advance_func_t: + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the advance for a specified glyph, in + * vertical-direction text segments. Advances must be returned in + * an #hb_position_t output parameter. + * + **/ typedef hb_font_get_glyph_advance_func_t hb_font_get_glyph_v_advance_func_t; +/** + * hb_font_get_glyph_advances_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @count: The number of glyph IDs in the sequence queried + * @first_glyph: The first glyph ID to query + * @glyph_stride: The stride between successive glyph IDs + * @first_advance: (out): The first advance retrieved + * @advance_stride: The stride between successive advances + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the advances for a sequence of glyphs. + * + **/ +typedef void (*hb_font_get_glyph_advances_func_t) (hb_font_t* font, void* font_data, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride, + void *user_data); + +/** + * hb_font_get_glyph_h_advances_func_t: + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the advances for a sequence of glyphs, in + * horizontal-direction text segments. + * + **/ +typedef hb_font_get_glyph_advances_func_t hb_font_get_glyph_h_advances_func_t; + +/** + * hb_font_get_glyph_v_advances_func_t: + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the advances for a sequence of glyphs, in + * vertical-direction text segments. + * + **/ +typedef hb_font_get_glyph_advances_func_t hb_font_get_glyph_v_advances_func_t; + +/** + * hb_font_get_glyph_origin_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @glyph: The glyph ID to query + * @x: (out): The X coordinate of the origin + * @y: (out): The Y coordinate of the origin + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the (X,Y) coordinates (in font units) of the + * origin for a glyph. Each coordinate must be returned in an #hb_position_t + * output parameter. + * + * Return value: `true` if data found, `false` otherwise + * + **/ typedef hb_bool_t (*hb_font_get_glyph_origin_func_t) (hb_font_t *font, void *font_data, hb_codepoint_t glyph, hb_position_t *x, hb_position_t *y, void *user_data); + +/** + * hb_font_get_glyph_h_origin_func_t: + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the (X,Y) coordinates (in font units) of the + * origin for a glyph, for horizontal-direction text segments. Each + * coordinate must be returned in an #hb_position_t output parameter. + * + **/ typedef hb_font_get_glyph_origin_func_t hb_font_get_glyph_h_origin_func_t; + +/** + * hb_font_get_glyph_v_origin_func_t: + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the (X,Y) coordinates (in font units) of the + * origin for a glyph, for vertical-direction text segments. Each coordinate + * must be returned in an #hb_position_t output parameter. + * + **/ typedef hb_font_get_glyph_origin_func_t hb_font_get_glyph_v_origin_func_t; +/** + * hb_font_get_glyph_kerning_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @first_glyph: The glyph ID of the first glyph in the glyph pair + * @second_glyph: The glyph ID of the second glyph in the glyph pair + * @user_data: User data pointer passed by the caller + * + * This method should retrieve the kerning-adjustment value for a glyph-pair in + * the specified font, for horizontal text segments. + * + **/ typedef hb_position_t (*hb_font_get_glyph_kerning_func_t) (hb_font_t *font, void *font_data, hb_codepoint_t first_glyph, hb_codepoint_t second_glyph, void *user_data); +/** + * hb_font_get_glyph_h_kerning_func_t: + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the kerning-adjustment value for a glyph-pair in + * the specified font, for horizontal text segments. + * + **/ typedef hb_font_get_glyph_kerning_func_t hb_font_get_glyph_h_kerning_func_t; -typedef hb_font_get_glyph_kerning_func_t hb_font_get_glyph_v_kerning_func_t; +/** + * hb_font_get_glyph_extents_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @glyph: The glyph ID to query + * @extents: (out): The #hb_glyph_extents_t retrieved + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the extents for a specified glyph. Extents must be + * returned in an #hb_glyph_extents output parameter. + * + * Return value: `true` if data found, `false` otherwise + * + **/ typedef hb_bool_t (*hb_font_get_glyph_extents_func_t) (hb_font_t *font, void *font_data, hb_codepoint_t glyph, hb_glyph_extents_t *extents, void *user_data); + +/** + * hb_font_get_glyph_contour_point_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @glyph: The glyph ID to query + * @point_index: The contour-point index to query + * @x: (out): The X value retrieved for the contour point + * @y: (out): The Y value retrieved for the contour point + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the (X,Y) coordinates (in font units) for a + * specified contour point in a glyph. Each coordinate must be returned as + * an #hb_position_t output parameter. + * + * Return value: `true` if data found, `false` otherwise + * + **/ typedef hb_bool_t (*hb_font_get_glyph_contour_point_func_t) (hb_font_t *font, void *font_data, hb_codepoint_t glyph, unsigned int point_index, hb_position_t *x, hb_position_t *y, void *user_data); +/** + * hb_font_get_glyph_name_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @glyph: The glyph ID to query + * @name: (out) (array length=size): Name string retrieved for the glyph ID + * @size: Length of the glyph-name string retrieved + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the glyph name that corresponds to a + * glyph ID. The name should be returned in a string output parameter. + * + * Return value: `true` if data found, `false` otherwise + * + **/ typedef hb_bool_t (*hb_font_get_glyph_name_func_t) (hb_font_t *font, void *font_data, hb_codepoint_t glyph, char *name, unsigned int size, void *user_data); + +/** + * hb_font_get_glyph_from_name_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @name: (array length=len): The name string to query + * @len: The length of the name queried + * @glyph: (out): The glyph ID retrieved + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the glyph ID that corresponds to a glyph-name + * string. + * + * Return value: `true` if data found, `false` otherwise + * + **/ typedef hb_bool_t (*hb_font_get_glyph_from_name_func_t) (hb_font_t *font, void *font_data, const char *name, int len, /* -1 means nul-terminated */ hb_codepoint_t *glyph, void *user_data); +/** + * hb_font_draw_glyph_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @glyph: The glyph ID to query + * @draw_funcs: The draw functions to send the shape data to + * @draw_data: The data accompanying the draw functions + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * Since: 7.0.0 + * + **/ +typedef void (*hb_font_draw_glyph_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, void *draw_data, + void *user_data); + +/** + * hb_font_paint_glyph_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @glyph: The glyph ID to query + * @paint_funcs: The paint functions to use + * @paint_data: The data accompanying the paint functions + * @palette_index: The color palette to use + * @foreground: The foreground color + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * Since: 7.0.0 + */ +typedef void (*hb_font_paint_glyph_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, + hb_paint_funcs_t *paint_funcs, void *paint_data, + unsigned int palette_index, + hb_color_t foreground, + void *user_data); /* func setters */ /** * hb_font_funcs_set_font_h_extents_func: - * @ffuncs: font functions. - * @func: (closure user_data) (destroy destroy) (scope notified): - * @user_data: - * @destroy: - * + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore * + * Sets the implementation function for #hb_font_get_font_h_extents_func_t. * * Since: 1.1.2 **/ @@ -186,12 +546,12 @@ hb_font_funcs_set_font_h_extents_func (hb_font_funcs_t *ffuncs, /** * hb_font_funcs_set_font_v_extents_func: - * @ffuncs: font functions. - * @func: (closure user_data) (destroy destroy) (scope notified): - * @user_data: - * @destroy: - * + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore * + * Sets the implementation function for #hb_font_get_font_v_extents_func_t. * * Since: 1.1.2 **/ @@ -202,12 +562,12 @@ hb_font_funcs_set_font_v_extents_func (hb_font_funcs_t *ffuncs, /** * hb_font_funcs_set_nominal_glyph_func: - * @ffuncs: font functions. - * @func: (closure user_data) (destroy destroy) (scope notified): - * @user_data: - * @destroy: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore * - * + * Sets the implementation function for #hb_font_get_nominal_glyph_func_t. * * Since: 1.2.3 **/ @@ -217,13 +577,29 @@ hb_font_funcs_set_nominal_glyph_func (hb_font_funcs_t *ffuncs, void *user_data, hb_destroy_func_t destroy); /** - * hb_font_funcs_set_variation_glyph_func: - * @ffuncs: font functions. - * @func: (closure user_data) (destroy destroy) (scope notified): - * @user_data: - * @destroy: + * hb_font_funcs_set_nominal_glyphs_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore * - * + * Sets the implementation function for #hb_font_get_nominal_glyphs_func_t. + * + * Since: 2.0.0 + **/ +HB_EXTERN void +hb_font_funcs_set_nominal_glyphs_func (hb_font_funcs_t *ffuncs, + hb_font_get_nominal_glyphs_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_variation_glyph_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_font_get_variation_glyph_func_t. * * Since: 1.2.3 **/ @@ -234,12 +610,12 @@ hb_font_funcs_set_variation_glyph_func (hb_font_funcs_t *ffuncs, /** * hb_font_funcs_set_glyph_h_advance_func: - * @ffuncs: font functions. - * @func: (closure user_data) (destroy destroy) (scope notified): - * @user_data: - * @destroy: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore * - * + * Sets the implementation function for #hb_font_get_glyph_h_advance_func_t. * * Since: 0.9.2 **/ @@ -250,12 +626,12 @@ hb_font_funcs_set_glyph_h_advance_func (hb_font_funcs_t *ffuncs, /** * hb_font_funcs_set_glyph_v_advance_func: - * @ffuncs: font functions. - * @func: (closure user_data) (destroy destroy) (scope notified): - * @user_data: - * @destroy: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore * - * + * Sets the implementation function for #hb_font_get_glyph_v_advance_func_t. * * Since: 0.9.2 **/ @@ -265,13 +641,45 @@ hb_font_funcs_set_glyph_v_advance_func (hb_font_funcs_t *ffuncs, void *user_data, hb_destroy_func_t destroy); /** - * hb_font_funcs_set_glyph_h_origin_func: - * @ffuncs: font functions. - * @func: (closure user_data) (destroy destroy) (scope notified): - * @user_data: - * @destroy: + * hb_font_funcs_set_glyph_h_advances_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore * - * + * Sets the implementation function for #hb_font_get_glyph_h_advances_func_t. + * + * Since: 1.8.6 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_h_advances_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_h_advances_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_v_advances_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_font_get_glyph_v_advances_func_t. + * + * Since: 1.8.6 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_v_advances_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_v_advances_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_h_origin_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_font_get_glyph_h_origin_func_t. * * Since: 0.9.2 **/ @@ -282,12 +690,12 @@ hb_font_funcs_set_glyph_h_origin_func (hb_font_funcs_t *ffuncs, /** * hb_font_funcs_set_glyph_v_origin_func: - * @ffuncs: font functions. - * @func: (closure user_data) (destroy destroy) (scope notified): - * @user_data: - * @destroy: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore * - * + * Sets the implementation function for #hb_font_get_glyph_v_origin_func_t. * * Since: 0.9.2 **/ @@ -298,12 +706,12 @@ hb_font_funcs_set_glyph_v_origin_func (hb_font_funcs_t *ffuncs, /** * hb_font_funcs_set_glyph_h_kerning_func: - * @ffuncs: font functions. - * @func: (closure user_data) (destroy destroy) (scope notified): - * @user_data: - * @destroy: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore * - * + * Sets the implementation function for #hb_font_get_glyph_h_kerning_func_t. * * Since: 0.9.2 **/ @@ -312,30 +720,14 @@ hb_font_funcs_set_glyph_h_kerning_func (hb_font_funcs_t *ffuncs, hb_font_get_glyph_h_kerning_func_t func, void *user_data, hb_destroy_func_t destroy); -/** - * hb_font_funcs_set_glyph_v_kerning_func: - * @ffuncs: font functions. - * @func: (closure user_data) (destroy destroy) (scope notified): - * @user_data: - * @destroy: - * - * - * - * Since: 0.9.2 - **/ -HB_EXTERN void -hb_font_funcs_set_glyph_v_kerning_func (hb_font_funcs_t *ffuncs, - hb_font_get_glyph_v_kerning_func_t func, - void *user_data, hb_destroy_func_t destroy); - /** * hb_font_funcs_set_glyph_extents_func: - * @ffuncs: font functions. - * @func: (closure user_data) (destroy destroy) (scope notified): - * @user_data: - * @destroy: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore * - * + * Sets the implementation function for #hb_font_get_glyph_extents_func_t. * * Since: 0.9.2 **/ @@ -346,12 +738,12 @@ hb_font_funcs_set_glyph_extents_func (hb_font_funcs_t *ffuncs, /** * hb_font_funcs_set_glyph_contour_point_func: - * @ffuncs: font functions. - * @func: (closure user_data) (destroy destroy) (scope notified): - * @user_data: - * @destroy: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore * - * + * Sets the implementation function for #hb_font_get_glyph_contour_point_func_t. * * Since: 0.9.2 **/ @@ -362,12 +754,12 @@ hb_font_funcs_set_glyph_contour_point_func (hb_font_funcs_t *ffuncs, /** * hb_font_funcs_set_glyph_name_func: - * @ffuncs: font functions. - * @func: (closure user_data) (destroy destroy) (scope notified): - * @user_data: - * @destroy: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore * - * + * Sets the implementation function for #hb_font_get_glyph_name_func_t. * * Since: 0.9.2 **/ @@ -378,12 +770,12 @@ hb_font_funcs_set_glyph_name_func (hb_font_funcs_t *ffuncs, /** * hb_font_funcs_set_glyph_from_name_func: - * @ffuncs: font functions. - * @func: (closure user_data) (destroy destroy) (scope notified): - * @user_data: - * @destroy: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore * - * + * Sets the implementation function for #hb_font_get_glyph_from_name_func_t. * * Since: 0.9.2 **/ @@ -392,6 +784,38 @@ hb_font_funcs_set_glyph_from_name_func (hb_font_funcs_t *ffuncs, hb_font_get_glyph_from_name_func_t func, void *user_data, hb_destroy_func_t destroy); +/** + * hb_font_funcs_set_draw_glyph_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_font_draw_glyph_func_t. + * + * Since: 7.0.0 + **/ +HB_EXTERN void +hb_font_funcs_set_draw_glyph_func (hb_font_funcs_t *ffuncs, + hb_font_draw_glyph_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_paint_glyph_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is no longer needed + * + * Sets the implementation function for #hb_font_paint_glyph_func_t. + * + * Since: 7.0.0 + */ +HB_EXTERN void +hb_font_funcs_set_paint_glyph_func (hb_font_funcs_t *ffuncs, + hb_font_paint_glyph_func_t func, + void *user_data, hb_destroy_func_t destroy); + /* func dispatch */ HB_EXTERN hb_bool_t @@ -410,6 +834,14 @@ hb_font_get_variation_glyph (hb_font_t *font, hb_codepoint_t unicode, hb_codepoint_t variation_selector, hb_codepoint_t *glyph); +HB_EXTERN unsigned int +hb_font_get_nominal_glyphs (hb_font_t *font, + unsigned int count, + const hb_codepoint_t *first_unicode, + unsigned int unicode_stride, + hb_codepoint_t *first_glyph, + unsigned int glyph_stride); + HB_EXTERN hb_position_t hb_font_get_glyph_h_advance (hb_font_t *font, hb_codepoint_t glyph); @@ -417,6 +849,21 @@ HB_EXTERN hb_position_t hb_font_get_glyph_v_advance (hb_font_t *font, hb_codepoint_t glyph); +HB_EXTERN void +hb_font_get_glyph_h_advances (hb_font_t* font, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride); +HB_EXTERN void +hb_font_get_glyph_v_advances (hb_font_t* font, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride); + HB_EXTERN hb_bool_t hb_font_get_glyph_h_origin (hb_font_t *font, hb_codepoint_t glyph, @@ -429,9 +876,6 @@ hb_font_get_glyph_v_origin (hb_font_t *font, HB_EXTERN hb_position_t hb_font_get_glyph_h_kerning (hb_font_t *font, hb_codepoint_t left_glyph, hb_codepoint_t right_glyph); -HB_EXTERN hb_position_t -hb_font_get_glyph_v_kerning (hb_font_t *font, - hb_codepoint_t top_glyph, hb_codepoint_t bottom_glyph); HB_EXTERN hb_bool_t hb_font_get_glyph_extents (hb_font_t *font, @@ -452,11 +896,22 @@ hb_font_get_glyph_from_name (hb_font_t *font, const char *name, int len, /* -1 means nul-terminated */ hb_codepoint_t *glyph); +HB_EXTERN void +hb_font_draw_glyph (hb_font_t *font, + hb_codepoint_t glyph, + hb_draw_funcs_t *dfuncs, void *draw_data); + +HB_EXTERN void +hb_font_paint_glyph (hb_font_t *font, + hb_codepoint_t glyph, + hb_paint_funcs_t *pfuncs, void *paint_data, + unsigned int palette_index, + hb_color_t foreground); /* high-level funcs, with fallback */ /* Calls either hb_font_get_nominal_glyph() if variation_selector is 0, - * otherwise callse hb_font_get_variation_glyph(). */ + * otherwise calls hb_font_get_variation_glyph(). */ HB_EXTERN hb_bool_t hb_font_get_glyph (hb_font_t *font, hb_codepoint_t unicode, hb_codepoint_t variation_selector, @@ -472,6 +927,14 @@ hb_font_get_glyph_advance_for_direction (hb_font_t *font, hb_direction_t direction, hb_position_t *x, hb_position_t *y); HB_EXTERN void +hb_font_get_glyph_advances_for_direction (hb_font_t* font, + hb_direction_t direction, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride); +HB_EXTERN void hb_font_get_glyph_origin_for_direction (hb_font_t *font, hb_codepoint_t glyph, hb_direction_t direction, @@ -547,7 +1010,7 @@ hb_font_set_user_data (hb_font_t *font, HB_EXTERN void * -hb_font_get_user_data (hb_font_t *font, +hb_font_get_user_data (const hb_font_t *font, hb_user_data_key_t *key); HB_EXTERN void @@ -556,6 +1019,12 @@ hb_font_make_immutable (hb_font_t *font); HB_EXTERN hb_bool_t hb_font_is_immutable (hb_font_t *font); +HB_EXTERN unsigned int +hb_font_get_serial (hb_font_t *font); + +HB_EXTERN void +hb_font_changed (hb_font_t *font); + HB_EXTERN void hb_font_set_parent (hb_font_t *font, hb_font_t *parent); @@ -580,8 +1049,8 @@ hb_font_set_funcs (hb_font_t *font, /* Be *very* careful with this function! */ HB_EXTERN void hb_font_set_funcs_data (hb_font_t *font, - void *font_data, - hb_destroy_func_t destroy); + void *font_data, + hb_destroy_func_t destroy); HB_EXTERN void @@ -617,16 +1086,41 @@ hb_font_set_ptem (hb_font_t *font, float ptem); HB_EXTERN float hb_font_get_ptem (hb_font_t *font); +HB_EXTERN void +hb_font_set_synthetic_bold (hb_font_t *font, + float x_embolden, float y_embolden, + hb_bool_t in_place); + +HB_EXTERN void +hb_font_get_synthetic_bold (hb_font_t *font, + float *x_embolden, float *y_embolden, + hb_bool_t *in_place); + +HB_EXTERN void +hb_font_set_synthetic_slant (hb_font_t *font, float slant); + +HB_EXTERN float +hb_font_get_synthetic_slant (hb_font_t *font); + HB_EXTERN void hb_font_set_variations (hb_font_t *font, const hb_variation_t *variations, unsigned int variations_length); +HB_EXTERN void +hb_font_set_variation (hb_font_t *font, + hb_tag_t tag, + float value); + HB_EXTERN void hb_font_set_var_coords_design (hb_font_t *font, const float *coords, unsigned int coords_length); +HB_EXTERN const float * +hb_font_get_var_coords_design (hb_font_t *font, + unsigned int *length); + HB_EXTERN void hb_font_set_var_coords_normalized (hb_font_t *font, const int *coords, /* 2.14 normalized */ @@ -636,6 +1130,24 @@ HB_EXTERN const int * hb_font_get_var_coords_normalized (hb_font_t *font, unsigned int *length); +/** + * HB_FONT_NO_VAR_NAMED_INSTANCE: + * + * Constant signifying that a font does not have any + * named-instance index set. This is the default of + * a font. + * + * Since: 7.0.0 + */ +#define HB_FONT_NO_VAR_NAMED_INSTANCE 0xFFFFFFFF + +HB_EXTERN void +hb_font_set_var_named_instance (hb_font_t *font, + unsigned int instance_index); + +HB_EXTERN unsigned int +hb_font_get_var_named_instance (hb_font_t *font); + HB_END_DECLS #endif /* HB_FONT_H */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-font.hh b/src/3rdparty/harfbuzz-ng/src/hb-font.hh new file mode 100644 index 00000000000..f503575c34a --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-font.hh @@ -0,0 +1,720 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2011 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_FONT_HH +#define HB_FONT_HH + +#include "hb.hh" + +#include "hb-face.hh" +#include "hb-shaper.hh" + + +/* + * hb_font_funcs_t + */ + +#define HB_FONT_FUNCS_IMPLEMENT_CALLBACKS \ + HB_FONT_FUNC_IMPLEMENT (get_,font_h_extents) \ + HB_FONT_FUNC_IMPLEMENT (get_,font_v_extents) \ + HB_FONT_FUNC_IMPLEMENT (get_,nominal_glyph) \ + HB_FONT_FUNC_IMPLEMENT (get_,nominal_glyphs) \ + HB_FONT_FUNC_IMPLEMENT (get_,variation_glyph) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_h_advance) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_v_advance) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_h_advances) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_v_advances) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_h_origin) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_v_origin) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_h_kerning) \ + HB_IF_NOT_DEPRECATED (HB_FONT_FUNC_IMPLEMENT (get_,glyph_v_kerning)) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_extents) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_contour_point) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_name) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_from_name) \ + HB_FONT_FUNC_IMPLEMENT (,draw_glyph) \ + HB_FONT_FUNC_IMPLEMENT (,paint_glyph) \ + /* ^--- Add new callbacks here */ + +struct hb_font_funcs_t +{ + hb_object_header_t header; + + struct { +#define HB_FONT_FUNC_IMPLEMENT(get_,name) void *name; + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + } *user_data; + + struct { +#define HB_FONT_FUNC_IMPLEMENT(get_,name) hb_destroy_func_t name; + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + } *destroy; + + /* Don't access these directly. Call font->get_*() instead. */ + union get_t { + struct get_funcs_t { +#define HB_FONT_FUNC_IMPLEMENT(get_,name) hb_font_##get_##name##_func_t name; + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + } f; + void (*array[0 +#define HB_FONT_FUNC_IMPLEMENT(get_,name) +1 + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + ]) (); + } get; +}; +DECLARE_NULL_INSTANCE (hb_font_funcs_t); + + +/* + * hb_font_t + */ + +#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INSTANTIATE_SHAPERS(shaper, font); +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT + +struct hb_font_t +{ + hb_object_header_t header; + unsigned int serial; + unsigned int serial_coords; + + hb_font_t *parent; + hb_face_t *face; + + int32_t x_scale; + int32_t y_scale; + + float x_embolden; + float y_embolden; + bool embolden_in_place; + int32_t x_strength; /* x_embolden, in scaled units. */ + int32_t y_strength; /* y_embolden, in scaled units. */ + + float slant; + float slant_xy; + + float x_multf; + float y_multf; + int64_t x_mult; + int64_t y_mult; + + unsigned int x_ppem; + unsigned int y_ppem; + + float ptem; + + /* Font variation coordinates. */ + unsigned int instance_index; + unsigned int num_coords; + int *coords; + float *design_coords; + + hb_font_funcs_t *klass; + void *user_data; + hb_destroy_func_t destroy; + + hb_shaper_object_dataset_t data; /* Various shaper data. */ + + + /* Convert from font-space to user-space */ + int64_t dir_mult (hb_direction_t direction) + { return HB_DIRECTION_IS_VERTICAL(direction) ? y_mult : x_mult; } + hb_position_t em_scale_x (int16_t v) { return em_mult (v, x_mult); } + hb_position_t em_scale_y (int16_t v) { return em_mult (v, y_mult); } + hb_position_t em_scalef_x (float v) { return em_multf (v, x_multf); } + hb_position_t em_scalef_y (float v) { return em_multf (v, y_multf); } + float em_fscale_x (int16_t v) { return em_fmult (v, x_multf); } + float em_fscale_y (int16_t v) { return em_fmult (v, y_multf); } + float em_fscalef_x (float v) { return em_fmultf (v, x_multf); } + float em_fscalef_y (float v) { return em_fmultf (v, y_multf); } + hb_position_t em_scale_dir (int16_t v, hb_direction_t direction) + { return em_mult (v, dir_mult (direction)); } + + /* Convert from parent-font user-space to our user-space */ + hb_position_t parent_scale_x_distance (hb_position_t v) + { + if (unlikely (parent && parent->x_scale != x_scale)) + return (hb_position_t) (v * (int64_t) this->x_scale / this->parent->x_scale); + return v; + } + hb_position_t parent_scale_y_distance (hb_position_t v) + { + if (unlikely (parent && parent->y_scale != y_scale)) + return (hb_position_t) (v * (int64_t) this->y_scale / this->parent->y_scale); + return v; + } + hb_position_t parent_scale_x_position (hb_position_t v) + { return parent_scale_x_distance (v); } + hb_position_t parent_scale_y_position (hb_position_t v) + { return parent_scale_y_distance (v); } + + void parent_scale_distance (hb_position_t *x, hb_position_t *y) + { + *x = parent_scale_x_distance (*x); + *y = parent_scale_y_distance (*y); + } + void parent_scale_position (hb_position_t *x, hb_position_t *y) + { + *x = parent_scale_x_position (*x); + *y = parent_scale_y_position (*y); + } + + void scale_glyph_extents (hb_glyph_extents_t *extents) + { + float x1 = em_fscale_x (extents->x_bearing); + float y1 = em_fscale_y (extents->y_bearing); + float x2 = em_fscale_x (extents->x_bearing + extents->width); + float y2 = em_fscale_y (extents->y_bearing + extents->height); + + /* Apply slant. */ + if (slant_xy) + { + x1 += hb_min (y1 * slant_xy, y2 * slant_xy); + x2 += hb_max (y1 * slant_xy, y2 * slant_xy); + } + + extents->x_bearing = floorf (x1); + extents->y_bearing = floorf (y1); + extents->width = ceilf (x2) - extents->x_bearing; + extents->height = ceilf (y2) - extents->y_bearing; + + if (x_strength || y_strength) + { + /* Y */ + int y_shift = y_strength; + if (y_scale < 0) y_shift = -y_shift; + extents->y_bearing += y_shift; + extents->height -= y_shift; + + /* X */ + int x_shift = x_strength; + if (x_scale < 0) x_shift = -x_shift; + if (embolden_in_place) + extents->x_bearing -= x_shift / 2; + extents->width += x_shift; + } + } + + + /* Public getters */ + + HB_INTERNAL bool has_func (unsigned int i); + HB_INTERNAL bool has_func_set (unsigned int i); + + /* has_* ... */ +#define HB_FONT_FUNC_IMPLEMENT(get_,name) \ + bool \ + has_##name##_func () \ + { \ + hb_font_funcs_t *funcs = this->klass; \ + unsigned int i = offsetof (hb_font_funcs_t::get_t::get_funcs_t, name) / sizeof (funcs->get.array[0]); \ + return has_func (i); \ + } \ + bool \ + has_##name##_func_set () \ + { \ + hb_font_funcs_t *funcs = this->klass; \ + unsigned int i = offsetof (hb_font_funcs_t::get_t::get_funcs_t, name) / sizeof (funcs->get.array[0]); \ + return has_func_set (i); \ + } + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + + hb_bool_t get_font_h_extents (hb_font_extents_t *extents) + { + hb_memset (extents, 0, sizeof (*extents)); + return klass->get.f.font_h_extents (this, user_data, + extents, + !klass->user_data ? nullptr : klass->user_data->font_h_extents); + } + hb_bool_t get_font_v_extents (hb_font_extents_t *extents) + { + hb_memset (extents, 0, sizeof (*extents)); + return klass->get.f.font_v_extents (this, user_data, + extents, + !klass->user_data ? nullptr : klass->user_data->font_v_extents); + } + + bool has_glyph (hb_codepoint_t unicode) + { + hb_codepoint_t glyph; + return get_nominal_glyph (unicode, &glyph); + } + + hb_bool_t get_nominal_glyph (hb_codepoint_t unicode, + hb_codepoint_t *glyph, + hb_codepoint_t not_found = 0) + { + *glyph = not_found; + return klass->get.f.nominal_glyph (this, user_data, + unicode, glyph, + !klass->user_data ? nullptr : klass->user_data->nominal_glyph); + } + unsigned int get_nominal_glyphs (unsigned int count, + const hb_codepoint_t *first_unicode, + unsigned int unicode_stride, + hb_codepoint_t *first_glyph, + unsigned int glyph_stride) + { + return klass->get.f.nominal_glyphs (this, user_data, + count, + first_unicode, unicode_stride, + first_glyph, glyph_stride, + !klass->user_data ? nullptr : klass->user_data->nominal_glyphs); + } + + hb_bool_t get_variation_glyph (hb_codepoint_t unicode, hb_codepoint_t variation_selector, + hb_codepoint_t *glyph, + hb_codepoint_t not_found = 0) + { + *glyph = not_found; + return klass->get.f.variation_glyph (this, user_data, + unicode, variation_selector, glyph, + !klass->user_data ? nullptr : klass->user_data->variation_glyph); + } + + hb_position_t get_glyph_h_advance (hb_codepoint_t glyph) + { + return klass->get.f.glyph_h_advance (this, user_data, + glyph, + !klass->user_data ? nullptr : klass->user_data->glyph_h_advance); + } + + hb_position_t get_glyph_v_advance (hb_codepoint_t glyph) + { + return klass->get.f.glyph_v_advance (this, user_data, + glyph, + !klass->user_data ? nullptr : klass->user_data->glyph_v_advance); + } + + void get_glyph_h_advances (unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + hb_position_t *first_advance, + unsigned int advance_stride) + { + return klass->get.f.glyph_h_advances (this, user_data, + count, + first_glyph, glyph_stride, + first_advance, advance_stride, + !klass->user_data ? nullptr : klass->user_data->glyph_h_advances); + } + + void get_glyph_v_advances (unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + hb_position_t *first_advance, + unsigned int advance_stride) + { + return klass->get.f.glyph_v_advances (this, user_data, + count, + first_glyph, glyph_stride, + first_advance, advance_stride, + !klass->user_data ? nullptr : klass->user_data->glyph_v_advances); + } + + hb_bool_t get_glyph_h_origin (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + *x = *y = 0; + return klass->get.f.glyph_h_origin (this, user_data, + glyph, x, y, + !klass->user_data ? nullptr : klass->user_data->glyph_h_origin); + } + + hb_bool_t get_glyph_v_origin (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + *x = *y = 0; + return klass->get.f.glyph_v_origin (this, user_data, + glyph, x, y, + !klass->user_data ? nullptr : klass->user_data->glyph_v_origin); + } + + hb_position_t get_glyph_h_kerning (hb_codepoint_t left_glyph, + hb_codepoint_t right_glyph) + { +#ifdef HB_DISABLE_DEPRECATED + return 0; +#else + return klass->get.f.glyph_h_kerning (this, user_data, + left_glyph, right_glyph, + !klass->user_data ? nullptr : klass->user_data->glyph_h_kerning); +#endif + } + + hb_position_t get_glyph_v_kerning (hb_codepoint_t top_glyph, + hb_codepoint_t bottom_glyph) + { +#ifdef HB_DISABLE_DEPRECATED + return 0; +#else + return klass->get.f.glyph_v_kerning (this, user_data, + top_glyph, bottom_glyph, + !klass->user_data ? nullptr : klass->user_data->glyph_v_kerning); +#endif + } + + hb_bool_t get_glyph_extents (hb_codepoint_t glyph, + hb_glyph_extents_t *extents) + { + hb_memset (extents, 0, sizeof (*extents)); + return klass->get.f.glyph_extents (this, user_data, + glyph, + extents, + !klass->user_data ? nullptr : klass->user_data->glyph_extents); + } + + hb_bool_t get_glyph_contour_point (hb_codepoint_t glyph, unsigned int point_index, + hb_position_t *x, hb_position_t *y) + { + *x = *y = 0; + return klass->get.f.glyph_contour_point (this, user_data, + glyph, point_index, + x, y, + !klass->user_data ? nullptr : klass->user_data->glyph_contour_point); + } + + hb_bool_t get_glyph_name (hb_codepoint_t glyph, + char *name, unsigned int size) + { + if (size) *name = '\0'; + return klass->get.f.glyph_name (this, user_data, + glyph, + name, size, + !klass->user_data ? nullptr : klass->user_data->glyph_name); + } + + hb_bool_t get_glyph_from_name (const char *name, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph) + { + *glyph = 0; + if (len == -1) len = strlen (name); + return klass->get.f.glyph_from_name (this, user_data, + name, len, + glyph, + !klass->user_data ? nullptr : klass->user_data->glyph_from_name); + } + + void draw_glyph (hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, void *draw_data) + { + klass->get.f.draw_glyph (this, user_data, + glyph, + draw_funcs, draw_data, + !klass->user_data ? nullptr : klass->user_data->draw_glyph); + } + + void paint_glyph (hb_codepoint_t glyph, + hb_paint_funcs_t *paint_funcs, void *paint_data, + unsigned int palette, + hb_color_t foreground) + { + klass->get.f.paint_glyph (this, user_data, + glyph, + paint_funcs, paint_data, + palette, foreground, + !klass->user_data ? nullptr : klass->user_data->paint_glyph); + } + + /* A bit higher-level, and with fallback */ + + void get_h_extents_with_fallback (hb_font_extents_t *extents) + { + if (!get_font_h_extents (extents)) + { + extents->ascender = y_scale * .8; + extents->descender = extents->ascender - y_scale; + extents->line_gap = 0; + } + } + void get_v_extents_with_fallback (hb_font_extents_t *extents) + { + if (!get_font_v_extents (extents)) + { + extents->ascender = x_scale / 2; + extents->descender = extents->ascender - x_scale; + extents->line_gap = 0; + } + } + + void get_extents_for_direction (hb_direction_t direction, + hb_font_extents_t *extents) + { + if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) + get_h_extents_with_fallback (extents); + else + get_v_extents_with_fallback (extents); + } + + void get_glyph_advance_for_direction (hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) + { + *x = *y = 0; + if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) + *x = get_glyph_h_advance (glyph); + else + *y = get_glyph_v_advance (glyph); + } + void get_glyph_advances_for_direction (hb_direction_t direction, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride) + { + if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) + get_glyph_h_advances (count, first_glyph, glyph_stride, first_advance, advance_stride); + else + get_glyph_v_advances (count, first_glyph, glyph_stride, first_advance, advance_stride); + } + + void guess_v_origin_minus_h_origin (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + *x = get_glyph_h_advance (glyph) / 2; + + hb_font_extents_t extents; + get_h_extents_with_fallback (&extents); + *y = extents.ascender; + } + + void get_glyph_h_origin_with_fallback (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + if (!get_glyph_h_origin (glyph, x, y) && + get_glyph_v_origin (glyph, x, y)) + { + hb_position_t dx, dy; + guess_v_origin_minus_h_origin (glyph, &dx, &dy); + *x -= dx; *y -= dy; + } + } + void get_glyph_v_origin_with_fallback (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + if (!get_glyph_v_origin (glyph, x, y) && + get_glyph_h_origin (glyph, x, y)) + { + hb_position_t dx, dy; + guess_v_origin_minus_h_origin (glyph, &dx, &dy); + *x += dx; *y += dy; + } + } + + void get_glyph_origin_for_direction (hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) + { + if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) + get_glyph_h_origin_with_fallback (glyph, x, y); + else + get_glyph_v_origin_with_fallback (glyph, x, y); + } + + void add_glyph_h_origin (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + hb_position_t origin_x, origin_y; + + get_glyph_h_origin_with_fallback (glyph, &origin_x, &origin_y); + + *x += origin_x; + *y += origin_y; + } + void add_glyph_v_origin (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + hb_position_t origin_x, origin_y; + + get_glyph_v_origin_with_fallback (glyph, &origin_x, &origin_y); + + *x += origin_x; + *y += origin_y; + } + void add_glyph_origin_for_direction (hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) + { + hb_position_t origin_x, origin_y; + + get_glyph_origin_for_direction (glyph, direction, &origin_x, &origin_y); + + *x += origin_x; + *y += origin_y; + } + + void subtract_glyph_h_origin (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + hb_position_t origin_x, origin_y; + + get_glyph_h_origin_with_fallback (glyph, &origin_x, &origin_y); + + *x -= origin_x; + *y -= origin_y; + } + void subtract_glyph_v_origin (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + hb_position_t origin_x, origin_y; + + get_glyph_v_origin_with_fallback (glyph, &origin_x, &origin_y); + + *x -= origin_x; + *y -= origin_y; + } + void subtract_glyph_origin_for_direction (hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) + { + hb_position_t origin_x, origin_y; + + get_glyph_origin_for_direction (glyph, direction, &origin_x, &origin_y); + + *x -= origin_x; + *y -= origin_y; + } + + void get_glyph_kerning_for_direction (hb_codepoint_t first_glyph, hb_codepoint_t second_glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) + { + if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) { + *y = 0; + *x = get_glyph_h_kerning (first_glyph, second_glyph); + } else { + *x = 0; + *y = get_glyph_v_kerning (first_glyph, second_glyph); + } + } + + hb_bool_t get_glyph_extents_for_origin (hb_codepoint_t glyph, + hb_direction_t direction, + hb_glyph_extents_t *extents) + { + hb_bool_t ret = get_glyph_extents (glyph, extents); + + if (ret) + subtract_glyph_origin_for_direction (glyph, direction, &extents->x_bearing, &extents->y_bearing); + + return ret; + } + + hb_bool_t get_glyph_contour_point_for_origin (hb_codepoint_t glyph, unsigned int point_index, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) + { + hb_bool_t ret = get_glyph_contour_point (glyph, point_index, x, y); + + if (ret) + subtract_glyph_origin_for_direction (glyph, direction, x, y); + + return ret; + } + + /* Generates gidDDD if glyph has no name. */ + void + glyph_to_string (hb_codepoint_t glyph, + char *s, unsigned int size) + { + if (get_glyph_name (glyph, s, size)) return; + + if (size && snprintf (s, size, "gid%u", glyph) < 0) + *s = '\0'; + } + + /* Parses gidDDD and uniUUUU strings automatically. */ + hb_bool_t + glyph_from_string (const char *s, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph) + { + if (get_glyph_from_name (s, len, glyph)) return true; + + if (len == -1) len = strlen (s); + + /* Straight glyph index. */ + if (hb_codepoint_parse (s, len, 10, glyph)) + return true; + + if (len > 3) + { + /* gidDDD syntax for glyph indices. */ + if (0 == strncmp (s, "gid", 3) && + hb_codepoint_parse (s + 3, len - 3, 10, glyph)) + return true; + + /* uniUUUU and other Unicode character indices. */ + hb_codepoint_t unichar; + if (0 == strncmp (s, "uni", 3) && + hb_codepoint_parse (s + 3, len - 3, 16, &unichar) && + get_nominal_glyph (unichar, glyph)) + return true; + } + + return false; + } + + void mults_changed () + { + float upem = face->get_upem (); + + x_multf = x_scale / upem; + y_multf = y_scale / upem; + bool x_neg = x_scale < 0; + x_mult = (x_neg ? -((int64_t) -x_scale << 16) : ((int64_t) x_scale << 16)) / upem; + bool y_neg = y_scale < 0; + y_mult = (y_neg ? -((int64_t) -y_scale << 16) : ((int64_t) y_scale << 16)) / upem; + + x_strength = fabsf (roundf (x_scale * x_embolden)); + y_strength = fabsf (roundf (y_scale * y_embolden)); + + slant_xy = y_scale ? slant * x_scale / y_scale : 0.f; + + data.fini (); + } + + hb_position_t em_mult (int16_t v, int64_t mult) + { return (hb_position_t) ((v * mult + 32768) >> 16); } + hb_position_t em_multf (float v, float mult) + { return (hb_position_t) roundf (em_fmultf (v, mult)); } + float em_fmultf (float v, float mult) + { return v * mult; } + float em_fmult (int16_t v, float mult) + { return (float) v * mult; } +}; +DECLARE_NULL_INSTANCE (hb_font_t); + + +#endif /* HB_FONT_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ft-colr.hh b/src/3rdparty/harfbuzz-ng/src/hb-ft-colr.hh new file mode 100644 index 00000000000..1afbbbb1836 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-ft-colr.hh @@ -0,0 +1,601 @@ +/* + * Copyright © 2022 Behdad Esfahbod + * + * 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. + */ + +#ifndef HB_FT_COLR_HH +#define HB_FT_COLR_HH + +#include "hb.hh" + +#include "hb-paint-extents.hh" + +#include FT_COLOR_H + + +static hb_paint_composite_mode_t +_hb_ft_paint_composite_mode (FT_Composite_Mode mode) +{ + switch (mode) + { + case FT_COLR_COMPOSITE_CLEAR: return HB_PAINT_COMPOSITE_MODE_CLEAR; + case FT_COLR_COMPOSITE_SRC: return HB_PAINT_COMPOSITE_MODE_SRC; + case FT_COLR_COMPOSITE_DEST: return HB_PAINT_COMPOSITE_MODE_DEST; + case FT_COLR_COMPOSITE_SRC_OVER: return HB_PAINT_COMPOSITE_MODE_SRC_OVER; + case FT_COLR_COMPOSITE_DEST_OVER: return HB_PAINT_COMPOSITE_MODE_DEST_OVER; + case FT_COLR_COMPOSITE_SRC_IN: return HB_PAINT_COMPOSITE_MODE_SRC_IN; + case FT_COLR_COMPOSITE_DEST_IN: return HB_PAINT_COMPOSITE_MODE_DEST_IN; + case FT_COLR_COMPOSITE_SRC_OUT: return HB_PAINT_COMPOSITE_MODE_SRC_OUT; + case FT_COLR_COMPOSITE_DEST_OUT: return HB_PAINT_COMPOSITE_MODE_DEST_OUT; + case FT_COLR_COMPOSITE_SRC_ATOP: return HB_PAINT_COMPOSITE_MODE_SRC_ATOP; + case FT_COLR_COMPOSITE_DEST_ATOP: return HB_PAINT_COMPOSITE_MODE_DEST_ATOP; + case FT_COLR_COMPOSITE_XOR: return HB_PAINT_COMPOSITE_MODE_XOR; + case FT_COLR_COMPOSITE_PLUS: return HB_PAINT_COMPOSITE_MODE_PLUS; + case FT_COLR_COMPOSITE_SCREEN: return HB_PAINT_COMPOSITE_MODE_SCREEN; + case FT_COLR_COMPOSITE_OVERLAY: return HB_PAINT_COMPOSITE_MODE_OVERLAY; + case FT_COLR_COMPOSITE_DARKEN: return HB_PAINT_COMPOSITE_MODE_DARKEN; + case FT_COLR_COMPOSITE_LIGHTEN: return HB_PAINT_COMPOSITE_MODE_LIGHTEN; + case FT_COLR_COMPOSITE_COLOR_DODGE: return HB_PAINT_COMPOSITE_MODE_COLOR_DODGE; + case FT_COLR_COMPOSITE_COLOR_BURN: return HB_PAINT_COMPOSITE_MODE_COLOR_BURN; + case FT_COLR_COMPOSITE_HARD_LIGHT: return HB_PAINT_COMPOSITE_MODE_HARD_LIGHT; + case FT_COLR_COMPOSITE_SOFT_LIGHT: return HB_PAINT_COMPOSITE_MODE_SOFT_LIGHT; + case FT_COLR_COMPOSITE_DIFFERENCE: return HB_PAINT_COMPOSITE_MODE_DIFFERENCE; + case FT_COLR_COMPOSITE_EXCLUSION: return HB_PAINT_COMPOSITE_MODE_EXCLUSION; + case FT_COLR_COMPOSITE_MULTIPLY: return HB_PAINT_COMPOSITE_MODE_MULTIPLY; + case FT_COLR_COMPOSITE_HSL_HUE: return HB_PAINT_COMPOSITE_MODE_HSL_HUE; + case FT_COLR_COMPOSITE_HSL_SATURATION: return HB_PAINT_COMPOSITE_MODE_HSL_SATURATION; + case FT_COLR_COMPOSITE_HSL_COLOR: return HB_PAINT_COMPOSITE_MODE_HSL_COLOR; + case FT_COLR_COMPOSITE_HSL_LUMINOSITY: return HB_PAINT_COMPOSITE_MODE_HSL_LUMINOSITY; + + case FT_COLR_COMPOSITE_MAX: HB_FALLTHROUGH; + default: return HB_PAINT_COMPOSITE_MODE_CLEAR; + } +} + +typedef struct hb_ft_paint_context_t hb_ft_paint_context_t; + +static void +_hb_ft_paint (hb_ft_paint_context_t *c, + FT_OpaquePaint opaque_paint); + +struct hb_ft_paint_context_t +{ + hb_ft_paint_context_t (const hb_ft_font_t *ft_font, + hb_font_t *font, + hb_paint_funcs_t *paint_funcs, void *paint_data, + FT_Color *palette, + unsigned palette_index, + hb_color_t foreground) : + ft_font (ft_font), font(font), + funcs (paint_funcs), data (paint_data), + palette (palette), palette_index (palette_index), foreground (foreground) {} + + void recurse (FT_OpaquePaint paint) + { + if (unlikely (depth_left <= 0 || edge_count <= 0)) return; + depth_left--; + edge_count--; + _hb_ft_paint (this, paint); + depth_left++; + } + + const hb_ft_font_t *ft_font; + hb_font_t *font; + hb_paint_funcs_t *funcs; + void *data; + FT_Color *palette; + unsigned palette_index; + hb_color_t foreground; + hb_map_t current_glyphs; + hb_map_t current_layers; + int depth_left = HB_MAX_NESTING_LEVEL; + int edge_count = HB_COLRV1_MAX_EDGE_COUNT; +}; + +static unsigned +_hb_ft_color_line_get_color_stops (hb_color_line_t *color_line, + void *color_line_data, + unsigned int start, + unsigned int *count, + hb_color_stop_t *color_stops, + void *user_data) +{ + FT_ColorLine *cl = (FT_ColorLine *) color_line_data; + hb_ft_paint_context_t *c = (hb_ft_paint_context_t *) user_data; + + if (count) + { + FT_ColorStop stop; + unsigned wrote = 0; + FT_ColorStopIterator iter = cl->color_stop_iterator; + + if (start >= cl->color_stop_iterator.num_color_stops) + { + *count = 0; + return cl->color_stop_iterator.num_color_stops; + } + + while (cl->color_stop_iterator.current_color_stop < start) + FT_Get_Colorline_Stops(c->ft_font->ft_face, + &stop, + &cl->color_stop_iterator); + + while (count && *count && + FT_Get_Colorline_Stops(c->ft_font->ft_face, + &stop, + &cl->color_stop_iterator)) + { + // https://github.com/harfbuzz/harfbuzz/issues/4013 + if (sizeof stop.stop_offset == 2) + color_stops->offset = stop.stop_offset / 16384.f; + else + color_stops->offset = stop.stop_offset / 65536.f; + + color_stops->is_foreground = stop.color.palette_index == 0xFFFF; + if (color_stops->is_foreground) + color_stops->color = HB_COLOR (hb_color_get_blue (c->foreground), + hb_color_get_green (c->foreground), + hb_color_get_red (c->foreground), + (hb_color_get_alpha (c->foreground) * stop.color.alpha) >> 14); + else + { + hb_color_t color; + if (c->funcs->custom_palette_color (c->data, stop.color.palette_index, &color)) + { + color_stops->color = HB_COLOR (hb_color_get_blue (color), + hb_color_get_green (color), + hb_color_get_red (color), + (hb_color_get_alpha (color) * stop.color.alpha) >> 14); + } + else + { + FT_Color ft_color = c->palette[stop.color.palette_index]; + color_stops->color = HB_COLOR (ft_color.blue, + ft_color.green, + ft_color.red, + (ft_color.alpha * stop.color.alpha) >> 14); + } + } + + color_stops++; + wrote++; + } + + *count = wrote; + + // reset the iterator for next time + cl->color_stop_iterator = iter; + } + + return cl->color_stop_iterator.num_color_stops; +} + +static hb_paint_extend_t +_hb_ft_color_line_get_extend (hb_color_line_t *color_line, + void *color_line_data, + void *user_data) +{ + FT_ColorLine *c = (FT_ColorLine *) color_line_data; + switch (c->extend) + { + default: + case FT_COLR_PAINT_EXTEND_PAD: return HB_PAINT_EXTEND_PAD; + case FT_COLR_PAINT_EXTEND_REPEAT: return HB_PAINT_EXTEND_REPEAT; + case FT_COLR_PAINT_EXTEND_REFLECT: return HB_PAINT_EXTEND_REFLECT; + } +} + +void +_hb_ft_paint (hb_ft_paint_context_t *c, + FT_OpaquePaint opaque_paint) +{ + FT_Face ft_face = c->ft_font->ft_face; + FT_COLR_Paint paint; + if (!FT_Get_Paint (ft_face, opaque_paint, &paint)) + return; + + switch (paint.format) + { + case FT_COLR_PAINTFORMAT_COLR_LAYERS: + { + FT_OpaquePaint other_paint = {0}; + while (FT_Get_Paint_Layers (ft_face, + &paint.u.colr_layers.layer_iterator, + &other_paint)) + { + unsigned i = paint.u.colr_layers.layer_iterator.layer; + + if (unlikely (c->current_layers.has (i))) + continue; + + c->current_layers.add (i); + + c->funcs->push_group (c->data); + c->recurse (other_paint); + c->funcs->pop_group (c->data, HB_PAINT_COMPOSITE_MODE_SRC_OVER); + + c->current_layers.del (i); + } + } + break; + case FT_COLR_PAINTFORMAT_SOLID: + { + bool is_foreground = paint.u.solid.color.palette_index == 0xFFFF; + hb_color_t color; + if (is_foreground) + color = HB_COLOR (hb_color_get_blue (c->foreground), + hb_color_get_green (c->foreground), + hb_color_get_red (c->foreground), + (hb_color_get_alpha (c->foreground) * paint.u.solid.color.alpha) >> 14); + else + { + if (c->funcs->custom_palette_color (c->data, paint.u.solid.color.palette_index, &color)) + { + color = HB_COLOR (hb_color_get_blue (color), + hb_color_get_green (color), + hb_color_get_red (color), + (hb_color_get_alpha (color) * paint.u.solid.color.alpha) >> 14); + } + else + { + FT_Color ft_color = c->palette[paint.u.solid.color.palette_index]; + color = HB_COLOR (ft_color.blue, + ft_color.green, + ft_color.red, + (ft_color.alpha * paint.u.solid.color.alpha) >> 14); + } + } + c->funcs->color (c->data, is_foreground, color); + } + break; + case FT_COLR_PAINTFORMAT_LINEAR_GRADIENT: + { + hb_color_line_t cl = { + &paint.u.linear_gradient.colorline, + _hb_ft_color_line_get_color_stops, c, + _hb_ft_color_line_get_extend, nullptr + }; + + c->funcs->linear_gradient (c->data, &cl, + paint.u.linear_gradient.p0.x / 65536.f, + paint.u.linear_gradient.p0.y / 65536.f, + paint.u.linear_gradient.p1.x / 65536.f, + paint.u.linear_gradient.p1.y / 65536.f, + paint.u.linear_gradient.p2.x / 65536.f, + paint.u.linear_gradient.p2.y / 65536.f); + } + break; + case FT_COLR_PAINTFORMAT_RADIAL_GRADIENT: + { + hb_color_line_t cl = { + &paint.u.linear_gradient.colorline, + _hb_ft_color_line_get_color_stops, c, + _hb_ft_color_line_get_extend, nullptr + }; + + c->funcs->radial_gradient (c->data, &cl, + paint.u.radial_gradient.c0.x / 65536.f, + paint.u.radial_gradient.c0.y / 65536.f, + paint.u.radial_gradient.r0 / 65536.f, + paint.u.radial_gradient.c1.x / 65536.f, + paint.u.radial_gradient.c1.y / 65536.f, + paint.u.radial_gradient.r1 / 65536.f); + } + break; + case FT_COLR_PAINTFORMAT_SWEEP_GRADIENT: + { + hb_color_line_t cl = { + &paint.u.linear_gradient.colorline, + _hb_ft_color_line_get_color_stops, c, + _hb_ft_color_line_get_extend, nullptr + }; + + c->funcs->sweep_gradient (c->data, &cl, + paint.u.sweep_gradient.center.x / 65536.f, + paint.u.sweep_gradient.center.y / 65536.f, + (paint.u.sweep_gradient.start_angle / 65536.f + 1) * HB_PI, + (paint.u.sweep_gradient.end_angle / 65536.f + 1) * HB_PI); + } + break; + case FT_COLR_PAINTFORMAT_GLYPH: + { + c->funcs->push_inverse_root_transform (c->data, c->font); + c->ft_font->lock.unlock (); + c->funcs->push_clip_glyph (c->data, paint.u.glyph.glyphID, c->font); + c->ft_font->lock.lock (); + c->funcs->push_root_transform (c->data, c->font); + c->recurse (paint.u.glyph.paint); + c->funcs->pop_transform (c->data); + c->funcs->pop_clip (c->data); + c->funcs->pop_transform (c->data); + } + break; + case FT_COLR_PAINTFORMAT_COLR_GLYPH: + { + hb_codepoint_t gid = paint.u.colr_glyph.glyphID; + + if (unlikely (c->current_glyphs.has (gid))) + return; + + c->current_glyphs.add (gid); + + c->funcs->push_inverse_root_transform (c->data, c->font); + c->ft_font->lock.unlock (); + if (c->funcs->color_glyph (c->data, gid, c->font)) + { + c->ft_font->lock.lock (); + c->funcs->pop_transform (c->data); + c->current_glyphs.del (gid); + return; + } + c->ft_font->lock.lock (); + c->funcs->pop_transform (c->data); + + FT_OpaquePaint other_paint = {0}; + if (FT_Get_Color_Glyph_Paint (ft_face, gid, + FT_COLOR_NO_ROOT_TRANSFORM, + &other_paint)) + { + bool has_clip_box; + FT_ClipBox clip_box; + has_clip_box = FT_Get_Color_Glyph_ClipBox (ft_face, paint.u.colr_glyph.glyphID, &clip_box); + + if (has_clip_box) + { + /* The FreeType ClipBox is in scaled coordinates, whereas we need + * unscaled clipbox here. Oh well... + */ + + float upem = c->font->face->get_upem (); + float xscale = upem / (c->font->x_scale ? c->font->x_scale : upem); + float yscale = upem / (c->font->y_scale ? c->font->y_scale : upem); + + c->funcs->push_clip_rectangle (c->data, + clip_box.bottom_left.x * xscale, + clip_box.bottom_left.y * yscale, + clip_box.top_right.x * xscale, + clip_box.top_right.y * yscale); + } + + c->recurse (other_paint); + + if (has_clip_box) + c->funcs->pop_clip (c->data); + + c->current_glyphs.del (gid); + } + } + break; + case FT_COLR_PAINTFORMAT_TRANSFORM: + { + c->funcs->push_transform (c->data, + paint.u.transform.affine.xx / 65536.f, + paint.u.transform.affine.yx / 65536.f, + paint.u.transform.affine.xy / 65536.f, + paint.u.transform.affine.yy / 65536.f, + paint.u.transform.affine.dx / 65536.f, + paint.u.transform.affine.dy / 65536.f); + c->recurse (paint.u.transform.paint); + c->funcs->pop_transform (c->data); + } + break; + case FT_COLR_PAINTFORMAT_TRANSLATE: + { + float dx = paint.u.translate.dx / 65536.f; + float dy = paint.u.translate.dy / 65536.f; + + bool p1 = c->funcs->push_translate (c->data, dx, dy); + c->recurse (paint.u.translate.paint); + if (p1) c->funcs->pop_transform (c->data); + } + break; + case FT_COLR_PAINTFORMAT_SCALE: + { + float dx = paint.u.scale.center_x / 65536.f; + float dy = paint.u.scale.center_y / 65536.f; + float sx = paint.u.scale.scale_x / 65536.f; + float sy = paint.u.scale.scale_y / 65536.f; + + bool p1 = c->funcs->push_translate (c->data, +dx, +dy); + bool p2 = c->funcs->push_scale (c->data, sx, sy); + bool p3 = c->funcs->push_translate (c->data, -dx, -dy); + c->recurse (paint.u.scale.paint); + if (p3) c->funcs->pop_transform (c->data); + if (p2) c->funcs->pop_transform (c->data); + if (p1) c->funcs->pop_transform (c->data); + } + break; + case FT_COLR_PAINTFORMAT_ROTATE: + { + float dx = paint.u.rotate.center_x / 65536.f; + float dy = paint.u.rotate.center_y / 65536.f; + float a = paint.u.rotate.angle / 65536.f; + + bool p1 = c->funcs->push_translate (c->data, +dx, +dy); + bool p2 = c->funcs->push_rotate (c->data, a); + bool p3 = c->funcs->push_translate (c->data, -dx, -dy); + c->recurse (paint.u.rotate.paint); + if (p3) c->funcs->pop_transform (c->data); + if (p2) c->funcs->pop_transform (c->data); + if (p1) c->funcs->pop_transform (c->data); + } + break; + case FT_COLR_PAINTFORMAT_SKEW: + { + float dx = paint.u.skew.center_x / 65536.f; + float dy = paint.u.skew.center_y / 65536.f; + float sx = paint.u.skew.x_skew_angle / 65536.f; + float sy = paint.u.skew.y_skew_angle / 65536.f; + + bool p1 = c->funcs->push_translate (c->data, +dx, +dy); + bool p2 = c->funcs->push_skew (c->data, sx, sy); + bool p3 = c->funcs->push_translate (c->data, -dx, -dy); + c->recurse (paint.u.skew.paint); + if (p3) c->funcs->pop_transform (c->data); + if (p2) c->funcs->pop_transform (c->data); + if (p1) c->funcs->pop_transform (c->data); + } + break; + case FT_COLR_PAINTFORMAT_COMPOSITE: + { + c->recurse (paint.u.composite.backdrop_paint); + c->funcs->push_group (c->data); + c->recurse (paint.u.composite.source_paint); + c->funcs->pop_group (c->data, _hb_ft_paint_composite_mode (paint.u.composite.composite_mode)); + } + break; + + case FT_COLR_PAINT_FORMAT_MAX: break; + default: HB_FALLTHROUGH; + case FT_COLR_PAINTFORMAT_UNSUPPORTED: break; + } +} + + +static bool +hb_ft_paint_glyph_colr (hb_font_t *font, + void *font_data, + hb_codepoint_t gid, + hb_paint_funcs_t *paint_funcs, void *paint_data, + unsigned int palette_index, + hb_color_t foreground, + void *user_data) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + FT_Face ft_face = ft_font->ft_face; + + /* Face is locked. */ + + FT_Error error; + FT_Color* palette; + FT_LayerIterator iterator; + + FT_Bool have_layers; + FT_UInt layer_glyph_index; + FT_UInt layer_color_index; + + error = FT_Palette_Select(ft_face, palette_index, &palette); + if (error) + palette = NULL; + + /* COLRv1 */ + FT_OpaquePaint paint = {0}; + if (FT_Get_Color_Glyph_Paint (ft_face, gid, + FT_COLOR_NO_ROOT_TRANSFORM, + &paint)) + { + hb_ft_paint_context_t c (ft_font, font, + paint_funcs, paint_data, + palette, palette_index, foreground); + c.current_glyphs.add (gid); + + bool is_bounded = true; + FT_ClipBox clip_box; + if (FT_Get_Color_Glyph_ClipBox (ft_face, gid, &clip_box)) + { + c.funcs->push_clip_rectangle (c.data, + clip_box.bottom_left.x + + roundf (hb_min (font->slant_xy * clip_box.bottom_left.y, + font->slant_xy * clip_box.top_left.y)), + clip_box.bottom_left.y, + clip_box.top_right.x + + roundf (hb_max (font->slant_xy * clip_box.bottom_right.y, + font->slant_xy * clip_box.top_right.y)), + clip_box.top_right.y); + } + else + { + + auto *extents_funcs = hb_paint_extents_get_funcs (); + hb_paint_extents_context_t extents_data; + hb_ft_paint_context_t ce (ft_font, font, + extents_funcs, &extents_data, + palette, palette_index, foreground); + ce.current_glyphs.add (gid); + ce.funcs->push_root_transform (ce.data, font); + ce.recurse (paint); + ce.funcs->pop_transform (ce.data); + hb_extents_t extents = extents_data.get_extents (); + is_bounded = extents_data.is_bounded (); + + c.funcs->push_clip_rectangle (c.data, + extents.xmin, + extents.ymin, + extents.xmax, + extents.ymax); + } + + c.funcs->push_root_transform (c.data, font); + + if (is_bounded) + c.recurse (paint); + + c.funcs->pop_transform (c.data); + c.funcs->pop_clip (c.data); + + return true; + } + + /* COLRv0 */ + iterator.p = NULL; + have_layers = FT_Get_Color_Glyph_Layer(ft_face, + gid, + &layer_glyph_index, + &layer_color_index, + &iterator); + + if (palette && have_layers) + { + do + { + hb_bool_t is_foreground = true; + hb_color_t color = foreground; + + if ( layer_color_index != 0xFFFF ) + { + FT_Color layer_color = palette[layer_color_index]; + color = HB_COLOR (layer_color.blue, + layer_color.green, + layer_color.red, + layer_color.alpha); + is_foreground = false; + } + + ft_font->lock.unlock (); + paint_funcs->push_clip_glyph (paint_data, layer_glyph_index, font); + ft_font->lock.lock (); + paint_funcs->color (paint_data, is_foreground, color); + paint_funcs->pop_clip (paint_data); + + } while (FT_Get_Color_Glyph_Layer(ft_face, + gid, + &layer_glyph_index, + &layer_color_index, + &iterator)); + return true; + } + + return false; +} + + +#endif /* HB_FT_COLR_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ft.cc b/src/3rdparty/harfbuzz-ng/src/hb-ft.cc new file mode 100644 index 00000000000..6ca3f854655 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-ft.cc @@ -0,0 +1,1500 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2009 Keith Stribley + * Copyright © 2015 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#ifdef HAVE_FREETYPE + +#include "hb-ft.h" + +#include "hb-cache.hh" +#include "hb-draw.hh" +#include "hb-font.hh" +#include "hb-machinery.hh" +#include "hb-ot-os2-table.hh" +#include "hb-ot-shaper-arabic-pua.hh" +#include "hb-paint.hh" + +#include FT_ADVANCES_H +#include FT_MULTIPLE_MASTERS_H +#include FT_OUTLINE_H +#include FT_TRUETYPE_TABLES_H +#include FT_SYNTHESIS_H +#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 21300 +#include FT_COLOR_H +#endif + + +/** + * SECTION:hb-ft + * @title: hb-ft + * @short_description: FreeType integration + * @include: hb-ft.h + * + * Functions for using HarfBuzz with the FreeType library. + * + * HarfBuzz supports using FreeType to provide face and + * font data. + * + * Note that FreeType is not thread-safe, therefore these + * functions are not thread-safe either. + **/ + + +/* TODO: + * + * In general, this file does a fine job of what it's supposed to do. + * There are, however, things that need more work: + * + * - FreeType works in 26.6 mode. Clients can decide to use that mode, and everything + * would work fine. However, we also abuse this API for performing in font-space, + * but don't pass the correct flags to FreeType. We just abuse the no-hinting mode + * for that, such that no rounding etc happens. As such, we don't set ppem, and + * pass NO_HINTING as load_flags. Would be much better to use NO_SCALE, and scale + * ourselves. + * + * - We don't handle / allow for emboldening / obliqueing. + * + * - In the future, we should add constructors to create fonts in font space? + */ + + +using hb_ft_advance_cache_t = hb_cache_t<16, 24, 8, false>; + +struct hb_ft_font_t +{ + int load_flags; + bool symbol; /* Whether selected cmap is symbol cmap. */ + bool unref; /* Whether to destroy ft_face when done. */ + bool transform; /* Whether to apply FT_Face's transform. */ + + mutable hb_mutex_t lock; /* Protects members below. */ + FT_Face ft_face; + mutable unsigned cached_serial; + mutable hb_ft_advance_cache_t advance_cache; +}; + +static hb_ft_font_t * +_hb_ft_font_create (FT_Face ft_face, bool symbol, bool unref) +{ + hb_ft_font_t *ft_font = (hb_ft_font_t *) hb_calloc (1, sizeof (hb_ft_font_t)); + if (unlikely (!ft_font)) return nullptr; + + ft_font->lock.init (); + ft_font->ft_face = ft_face; + ft_font->symbol = symbol; + ft_font->unref = unref; + + ft_font->load_flags = FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING; + + ft_font->cached_serial = (unsigned) -1; + new (&ft_font->advance_cache) hb_ft_advance_cache_t; + + return ft_font; +} + +static void +_hb_ft_face_destroy (void *data) +{ + FT_Done_Face ((FT_Face) data); +} + +static void +_hb_ft_font_destroy (void *data) +{ + hb_ft_font_t *ft_font = (hb_ft_font_t *) data; + + if (ft_font->unref) + _hb_ft_face_destroy (ft_font->ft_face); + + ft_font->lock.fini (); + + hb_free (ft_font); +} + + +/* hb_font changed, update FT_Face. */ +static void _hb_ft_hb_font_changed (hb_font_t *font, FT_Face ft_face) +{ + hb_ft_font_t *ft_font = (hb_ft_font_t *) font->user_data; + + float x_mult = 1.f, y_mult = 1.f; + + if (font->x_scale < 0) x_mult = -x_mult; + if (font->y_scale < 0) y_mult = -y_mult; + + if (FT_Set_Char_Size (ft_face, + abs (font->x_scale), abs (font->y_scale), + 0, 0 +#if 0 + font->x_ppem * 72 * 64 / font->x_scale, + font->y_ppem * 72 * 64 / font->y_scale +#endif + ) && ft_face->num_fixed_sizes) + { +#ifdef HAVE_FT_GET_TRANSFORM + /* Bitmap font, eg. bitmap color emoji. */ + /* Pick largest size? */ + int x_scale = ft_face->available_sizes[ft_face->num_fixed_sizes - 1].x_ppem; + int y_scale = ft_face->available_sizes[ft_face->num_fixed_sizes - 1].y_ppem; + FT_Set_Char_Size (ft_face, + x_scale, y_scale, + 0, 0); + + /* This contains the sign that was previously in x_mult/y_mult. */ + x_mult = (float) font->x_scale / x_scale; + y_mult = (float) font->y_scale / y_scale; +#endif + } + else + { /* Shrug */ } + + + if (x_mult != 1.f || y_mult != 1.f) + { + FT_Matrix matrix = { (int) roundf (x_mult * (1<<16)), 0, + 0, (int) roundf (y_mult * (1<<16))}; + FT_Set_Transform (ft_face, &matrix, nullptr); + ft_font->transform = true; + } + +#if defined(HAVE_FT_GET_VAR_BLEND_COORDINATES) && !defined(HB_NO_VAR) + unsigned int num_coords; + const float *coords = hb_font_get_var_coords_design (font, &num_coords); + if (num_coords) + { + FT_Fixed *ft_coords = (FT_Fixed *) hb_calloc (num_coords, sizeof (FT_Fixed)); + if (ft_coords) + { + for (unsigned int i = 0; i < num_coords; i++) + ft_coords[i] = coords[i] * 65536.f; + FT_Set_Var_Design_Coordinates (ft_face, num_coords, ft_coords); + hb_free (ft_coords); + } + } +#endif +} + +/* Check if hb_font changed, update FT_Face. */ +static inline bool +_hb_ft_hb_font_check_changed (hb_font_t *font, + const hb_ft_font_t *ft_font) +{ + if (font->serial != ft_font->cached_serial) + { + _hb_ft_hb_font_changed (font, ft_font->ft_face); + ft_font->advance_cache.clear (); + ft_font->cached_serial = font->serial; + return true; + } + return false; +} + + +/** + * hb_ft_font_set_load_flags: + * @font: #hb_font_t to work upon + * @load_flags: The FreeType load flags to set + * + * Sets the FT_Load_Glyph load flags for the specified #hb_font_t. + * + * For more information, see + * https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#ft_load_xxx + * + * This function works with #hb_font_t objects created by + * hb_ft_font_create() or hb_ft_font_create_referenced(). + * + * Since: 1.0.5 + **/ +void +hb_ft_font_set_load_flags (hb_font_t *font, int load_flags) +{ + if (hb_object_is_immutable (font)) + return; + + if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)) + return; + + hb_ft_font_t *ft_font = (hb_ft_font_t *) font->user_data; + + ft_font->load_flags = load_flags; +} + +/** + * hb_ft_font_get_load_flags: + * @font: #hb_font_t to work upon + * + * Fetches the FT_Load_Glyph load flags of the specified #hb_font_t. + * + * For more information, see + * https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#ft_load_xxx + * + * This function works with #hb_font_t objects created by + * hb_ft_font_create() or hb_ft_font_create_referenced(). + * + * Return value: FT_Load_Glyph flags found, or 0 + * + * Since: 1.0.5 + **/ +int +hb_ft_font_get_load_flags (hb_font_t *font) +{ + if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)) + return 0; + + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data; + + return ft_font->load_flags; +} + +/** + * hb_ft_font_get_face: (skip) + * @font: #hb_font_t to work upon + * + * Fetches the FT_Face associated with the specified #hb_font_t + * font object. + * + * This function works with #hb_font_t objects created by + * hb_ft_font_create() or hb_ft_font_create_referenced(). + * + * Return value: (nullable): the FT_Face found or `NULL` + * + * Since: 0.9.2 + **/ +FT_Face +hb_ft_font_get_face (hb_font_t *font) +{ + if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)) + return nullptr; + + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data; + + return ft_font->ft_face; +} + +/** + * hb_ft_font_lock_face: (skip) + * @font: #hb_font_t to work upon + * + * Gets the FT_Face associated with @font. + * + * This face will be kept around and access to the FT_Face object + * from other HarfBuzz API wil be blocked until you call hb_ft_font_unlock_face(). + * + * This function works with #hb_font_t objects created by + * hb_ft_font_create() or hb_ft_font_create_referenced(). + * + * Return value: (nullable) (transfer none): the FT_Face associated with @font or `NULL` + * Since: 2.6.5 + **/ +FT_Face +hb_ft_font_lock_face (hb_font_t *font) +{ + if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)) + return nullptr; + + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data; + + ft_font->lock.lock (); + + return ft_font->ft_face; +} + +/** + * hb_ft_font_unlock_face: (skip) + * @font: #hb_font_t to work upon + * + * Releases an FT_Face previously obtained with hb_ft_font_lock_face(). + * + * Since: 2.6.5 + **/ +void +hb_ft_font_unlock_face (hb_font_t *font) +{ + if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)) + return; + + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data; + + ft_font->lock.unlock (); +} + + +static hb_bool_t +hb_ft_get_nominal_glyph (hb_font_t *font, + void *font_data, + hb_codepoint_t unicode, + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + unsigned int g = FT_Get_Char_Index (ft_font->ft_face, unicode); + + if (unlikely (!g)) + { + if (unlikely (ft_font->symbol)) + { + switch ((unsigned) font->face->table.OS2->get_font_page ()) { + case OT::OS2::font_page_t::FONT_PAGE_NONE: + if (unicode <= 0x00FFu) + /* For symbol-encoded OpenType fonts, we duplicate the + * U+F000..F0FF range at U+0000..U+00FF. That's what + * Windows seems to do, and that's hinted about at: + * https://docs.microsoft.com/en-us/typography/opentype/spec/recom + * under "Non-Standard (Symbol) Fonts". */ + g = FT_Get_Char_Index (ft_font->ft_face, 0xF000u + unicode); + break; +#ifndef HB_NO_OT_SHAPER_ARABIC_FALLBACK + case OT::OS2::font_page_t::FONT_PAGE_SIMP_ARABIC: + g = FT_Get_Char_Index (ft_font->ft_face, _hb_arabic_pua_simp_map (unicode)); + break; + case OT::OS2::font_page_t::FONT_PAGE_TRAD_ARABIC: + g = FT_Get_Char_Index (ft_font->ft_face, _hb_arabic_pua_trad_map (unicode)); + break; +#endif + default: + break; + } + if (!g) + return false; + } + else + return false; + } + + *glyph = g; + return true; +} + +static unsigned int +hb_ft_get_nominal_glyphs (hb_font_t *font HB_UNUSED, + void *font_data, + unsigned int count, + const hb_codepoint_t *first_unicode, + unsigned int unicode_stride, + hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + unsigned int done; + for (done = 0; + done < count && (*first_glyph = FT_Get_Char_Index (ft_font->ft_face, *first_unicode)); + done++) + { + first_unicode = &StructAtOffsetUnaligned (first_unicode, unicode_stride); + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + } + /* We don't need to do ft_font->symbol dance here, since HB calls the singular + * nominal_glyph() for what we don't handle here. */ + return done; +} + + +static hb_bool_t +hb_ft_get_variation_glyph (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t unicode, + hb_codepoint_t variation_selector, + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + unsigned int g = FT_Face_GetCharVariantIndex (ft_font->ft_face, unicode, variation_selector); + + if (unlikely (!g)) + return false; + + *glyph = g; + return true; +} + +static void +hb_ft_get_glyph_h_advances (hb_font_t* font, void* font_data, + unsigned count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_position_t *orig_first_advance = first_advance; + hb_lock_t lock (ft_font->lock); + FT_Face ft_face = ft_font->ft_face; + int load_flags = ft_font->load_flags; + float x_mult; +#ifdef HAVE_FT_GET_TRANSFORM + if (ft_font->transform) + { + FT_Matrix matrix; + FT_Get_Transform (ft_face, &matrix, nullptr); + x_mult = sqrtf ((float)matrix.xx * matrix.xx + (float)matrix.xy * matrix.xy) / 65536.f; + x_mult *= font->x_scale < 0 ? -1 : +1; + } + else +#endif + { + x_mult = font->x_scale < 0 ? -1 : +1; + } + + for (unsigned int i = 0; i < count; i++) + { + FT_Fixed v = 0; + hb_codepoint_t glyph = *first_glyph; + + unsigned int cv; + if (ft_font->advance_cache.get (glyph, &cv)) + v = cv; + else + { + FT_Get_Advance (ft_face, glyph, load_flags, &v); + /* Work around bug that FreeType seems to return negative advance + * for variable-set fonts if x_scale is negative! */ + v = abs (v); + v = (int) (v * x_mult + (1<<9)) >> 10; + ft_font->advance_cache.set (glyph, v); + } + + *first_advance = v; + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + + if (font->x_strength && !font->embolden_in_place) + { + /* Emboldening. */ + hb_position_t x_strength = font->x_scale >= 0 ? font->x_strength : -font->x_strength; + first_advance = orig_first_advance; + for (unsigned int i = 0; i < count; i++) + { + *first_advance += *first_advance ? x_strength : 0; + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + } +} + +#ifndef HB_NO_VERTICAL +static hb_position_t +hb_ft_get_glyph_v_advance (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + FT_Fixed v; + float y_mult; +#ifdef HAVE_FT_GET_TRANSFORM + if (ft_font->transform) + { + FT_Matrix matrix; + FT_Get_Transform (ft_font->ft_face, &matrix, nullptr); + y_mult = sqrtf ((float)matrix.yx * matrix.yx + (float)matrix.yy * matrix.yy) / 65536.f; + y_mult *= font->y_scale < 0 ? -1 : +1; + } + else +#endif + { + y_mult = font->y_scale < 0 ? -1 : +1; + } + + if (unlikely (FT_Get_Advance (ft_font->ft_face, glyph, ft_font->load_flags | FT_LOAD_VERTICAL_LAYOUT, &v))) + return 0; + + v = (int) (y_mult * v); + + /* Note: FreeType's vertical metrics grows downward while other FreeType coordinates + * have a Y growing upward. Hence the extra negation. */ + + hb_position_t y_strength = font->y_scale >= 0 ? font->y_strength : -font->y_strength; + return ((-v + (1<<9)) >> 10) + (font->embolden_in_place ? 0 : y_strength); +} +#endif + +#ifndef HB_NO_VERTICAL +static hb_bool_t +hb_ft_get_glyph_v_origin (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + FT_Face ft_face = ft_font->ft_face; + float x_mult, y_mult; +#ifdef HAVE_FT_GET_TRANSFORM + if (ft_font->transform) + { + FT_Matrix matrix; + FT_Get_Transform (ft_face, &matrix, nullptr); + x_mult = sqrtf ((float)matrix.xx * matrix.xx + (float)matrix.xy * matrix.xy) / 65536.f; + x_mult *= font->x_scale < 0 ? -1 : +1; + y_mult = sqrtf ((float)matrix.yx * matrix.yx + (float)matrix.yy * matrix.yy) / 65536.f; + y_mult *= font->y_scale < 0 ? -1 : +1; + } + else +#endif + { + x_mult = font->x_scale < 0 ? -1 : +1; + y_mult = font->y_scale < 0 ? -1 : +1; + } + + if (unlikely (FT_Load_Glyph (ft_face, glyph, ft_font->load_flags))) + return false; + + /* Note: FreeType's vertical metrics grows downward while other FreeType coordinates + * have a Y growing upward. Hence the extra negation. */ + *x = ft_face->glyph->metrics.horiBearingX - ft_face->glyph->metrics.vertBearingX; + *y = ft_face->glyph->metrics.horiBearingY - (-ft_face->glyph->metrics.vertBearingY); + + *x = (hb_position_t) (x_mult * *x); + *y = (hb_position_t) (y_mult * *y); + + return true; +} +#endif + +#ifndef HB_NO_OT_SHAPE_FALLBACK +static hb_position_t +hb_ft_get_glyph_h_kerning (hb_font_t *font, + void *font_data, + hb_codepoint_t left_glyph, + hb_codepoint_t right_glyph, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + FT_Vector kerningv; + + FT_Kerning_Mode mode = font->x_ppem ? FT_KERNING_DEFAULT : FT_KERNING_UNFITTED; + if (FT_Get_Kerning (ft_font->ft_face, left_glyph, right_glyph, mode, &kerningv)) + return 0; + + return kerningv.x; +} +#endif + +static hb_bool_t +hb_ft_get_glyph_extents (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + FT_Face ft_face = ft_font->ft_face; + float x_mult, y_mult; + float slant_xy = font->slant_xy; +#ifdef HAVE_FT_GET_TRANSFORM + if (ft_font->transform) + { + FT_Matrix matrix; + FT_Get_Transform (ft_face, &matrix, nullptr); + x_mult = sqrtf ((float)matrix.xx * matrix.xx + (float)matrix.xy * matrix.xy) / 65536.f; + x_mult *= font->x_scale < 0 ? -1 : +1; + y_mult = sqrtf ((float)matrix.yx * matrix.yx + (float)matrix.yy * matrix.yy) / 65536.f; + y_mult *= font->y_scale < 0 ? -1 : +1; + } + else +#endif + { + x_mult = font->x_scale < 0 ? -1 : +1; + y_mult = font->y_scale < 0 ? -1 : +1; + } + + if (unlikely (FT_Load_Glyph (ft_face, glyph, ft_font->load_flags))) + return false; + + /* Copied from hb_font_t::scale_glyph_extents. */ + + float x1 = x_mult * ft_face->glyph->metrics.horiBearingX; + float y1 = y_mult * ft_face->glyph->metrics.horiBearingY; + float x2 = x1 + x_mult * ft_face->glyph->metrics.width; + float y2 = y1 + y_mult * -ft_face->glyph->metrics.height; + + /* Apply slant. */ + if (slant_xy) + { + x1 += hb_min (y1 * slant_xy, y2 * slant_xy); + x2 += hb_max (y1 * slant_xy, y2 * slant_xy); + } + + extents->x_bearing = floorf (x1); + extents->y_bearing = floorf (y1); + extents->width = ceilf (x2) - extents->x_bearing; + extents->height = ceilf (y2) - extents->y_bearing; + + if (font->x_strength || font->y_strength) + { + /* Y */ + int y_shift = font->y_strength; + if (font->y_scale < 0) y_shift = -y_shift; + extents->y_bearing += y_shift; + extents->height -= y_shift; + + /* X */ + int x_shift = font->x_strength; + if (font->x_scale < 0) x_shift = -x_shift; + if (font->embolden_in_place) + extents->x_bearing -= x_shift / 2; + extents->width += x_shift; + } + + return true; +} + +static hb_bool_t +hb_ft_get_glyph_contour_point (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t glyph, + unsigned int point_index, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + FT_Face ft_face = ft_font->ft_face; + + if (unlikely (FT_Load_Glyph (ft_face, glyph, ft_font->load_flags))) + return false; + + if (unlikely (ft_face->glyph->format != FT_GLYPH_FORMAT_OUTLINE)) + return false; + + if (unlikely (point_index >= (unsigned int) ft_face->glyph->outline.n_points)) + return false; + + *x = ft_face->glyph->outline.points[point_index].x; + *y = ft_face->glyph->outline.points[point_index].y; + + return true; +} + +static hb_bool_t +hb_ft_get_glyph_name (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t glyph, + char *name, unsigned int size, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + FT_Face ft_face = ft_font->ft_face; + + hb_bool_t ret = !FT_Get_Glyph_Name (ft_face, glyph, name, size); + if (ret && (size && !*name)) + ret = false; + + return ret; +} + +static hb_bool_t +hb_ft_get_glyph_from_name (hb_font_t *font HB_UNUSED, + void *font_data, + const char *name, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + FT_Face ft_face = ft_font->ft_face; + + if (len < 0) + *glyph = FT_Get_Name_Index (ft_face, (FT_String *) name); + else { + /* Make a nul-terminated version. */ + char buf[128]; + len = hb_min (len, (int) sizeof (buf) - 1); + strncpy (buf, name, len); + buf[len] = '\0'; + *glyph = FT_Get_Name_Index (ft_face, buf); + } + + if (*glyph == 0) + { + /* Check whether the given name was actually the name of glyph 0. */ + char buf[128]; + if (!FT_Get_Glyph_Name(ft_face, 0, buf, sizeof (buf)) && + len < 0 ? !strcmp (buf, name) : !strncmp (buf, name, len)) + return true; + } + + return *glyph != 0; +} + +static hb_bool_t +hb_ft_get_font_h_extents (hb_font_t *font HB_UNUSED, + void *font_data, + hb_font_extents_t *metrics, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + FT_Face ft_face = ft_font->ft_face; + float y_mult; +#ifdef HAVE_FT_GET_TRANSFORM + if (ft_font->transform) + { + FT_Matrix matrix; + FT_Get_Transform (ft_face, &matrix, nullptr); + y_mult = sqrtf ((float)matrix.yx * matrix.yx + (float)matrix.yy * matrix.yy) / 65536.f; + y_mult *= font->y_scale < 0 ? -1 : +1; + } + else +#endif + { + y_mult = font->y_scale < 0 ? -1 : +1; + } + + if (ft_face->units_per_EM != 0) + { + metrics->ascender = FT_MulFix(ft_face->ascender, ft_face->size->metrics.y_scale); + metrics->descender = FT_MulFix(ft_face->descender, ft_face->size->metrics.y_scale); + metrics->line_gap = FT_MulFix( ft_face->height, ft_face->size->metrics.y_scale ) - (metrics->ascender - metrics->descender); + } + else + { + /* Bitmap-only font, eg. color bitmap font. */ + metrics->ascender = ft_face->size->metrics.ascender; + metrics->descender = ft_face->size->metrics.descender; + metrics->line_gap = ft_face->size->metrics.height - (metrics->ascender - metrics->descender); + } + + metrics->ascender = (hb_position_t) (y_mult * (metrics->ascender + font->y_strength)); + metrics->descender = (hb_position_t) (y_mult * metrics->descender); + metrics->line_gap = (hb_position_t) (y_mult * metrics->line_gap); + + return true; +} + +#ifndef HB_NO_DRAW + +static int +_hb_ft_move_to (const FT_Vector *to, + void *arg) +{ + hb_draw_session_t *drawing = (hb_draw_session_t *) arg; + drawing->move_to (to->x, to->y); + return FT_Err_Ok; +} + +static int +_hb_ft_line_to (const FT_Vector *to, + void *arg) +{ + hb_draw_session_t *drawing = (hb_draw_session_t *) arg; + drawing->line_to (to->x, to->y); + return FT_Err_Ok; +} + +static int +_hb_ft_conic_to (const FT_Vector *control, + const FT_Vector *to, + void *arg) +{ + hb_draw_session_t *drawing = (hb_draw_session_t *) arg; + drawing->quadratic_to (control->x, control->y, + to->x, to->y); + return FT_Err_Ok; +} + +static int +_hb_ft_cubic_to (const FT_Vector *control1, + const FT_Vector *control2, + const FT_Vector *to, + void *arg) +{ + hb_draw_session_t *drawing = (hb_draw_session_t *) arg; + drawing->cubic_to (control1->x, control1->y, + control2->x, control2->y, + to->x, to->y); + return FT_Err_Ok; +} + +static void +hb_ft_draw_glyph (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, void *draw_data, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + FT_Face ft_face = ft_font->ft_face; + + if (unlikely (FT_Load_Glyph (ft_face, glyph, + FT_LOAD_NO_BITMAP | ft_font->load_flags))) + return; + + if (ft_face->glyph->format != FT_GLYPH_FORMAT_OUTLINE) + return; + + const FT_Outline_Funcs outline_funcs = { + _hb_ft_move_to, + _hb_ft_line_to, + _hb_ft_conic_to, + _hb_ft_cubic_to, + 0, /* shift */ + 0, /* delta */ + }; + + hb_draw_session_t draw_session (draw_funcs, draw_data, font->slant_xy); + + /* Embolden */ + if (font->x_strength || font->y_strength) + { + FT_Outline_EmboldenXY (&ft_face->glyph->outline, font->x_strength, font->y_strength); + + int x_shift = 0; + int y_shift = 0; + if (font->embolden_in_place) + { + /* Undo the FreeType shift. */ + x_shift = -font->x_strength / 2; + y_shift = 0; + if (font->y_scale < 0) y_shift = -font->y_strength; + } + else + { + /* FreeType applied things in the wrong direction for negative scale; fix up. */ + if (font->x_scale < 0) x_shift = -font->x_strength; + if (font->y_scale < 0) y_shift = -font->y_strength; + } + if (x_shift || y_shift) + { + auto &outline = ft_face->glyph->outline; + for (auto &point : hb_iter (outline.points, outline.contours[outline.n_contours - 1] + 1)) + { + point.x += x_shift; + point.y += y_shift; + } + } + } + + + FT_Outline_Decompose (&ft_face->glyph->outline, + &outline_funcs, + &draw_session); +} +#endif + +#ifndef HB_NO_PAINT +#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 21300 + +#include "hb-ft-colr.hh" + +static void +hb_ft_paint_glyph (hb_font_t *font, + void *font_data, + hb_codepoint_t gid, + hb_paint_funcs_t *paint_funcs, void *paint_data, + unsigned int palette_index, + hb_color_t foreground, + void *user_data) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + FT_Face ft_face = ft_font->ft_face; + + /* We release the lock before calling into glyph callbacks, such that + * eg. draw API can call back into the face.*/ + + if (unlikely (FT_Load_Glyph (ft_face, gid, + ft_font->load_flags | FT_LOAD_COLOR))) + return; + + if (ft_face->glyph->format == FT_GLYPH_FORMAT_OUTLINE) + { + if (hb_ft_paint_glyph_colr (font, font_data, gid, + paint_funcs, paint_data, + palette_index, foreground, + user_data)) + return; + + /* Simple outline. */ + ft_font->lock.unlock (); + paint_funcs->push_clip_glyph (paint_data, gid, font); + ft_font->lock.lock (); + paint_funcs->color (paint_data, true, foreground); + paint_funcs->pop_clip (paint_data); + + return; + } + + auto *glyph = ft_face->glyph; + if (glyph->format == FT_GLYPH_FORMAT_BITMAP) + { + auto &bitmap = glyph->bitmap; + if (bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) + { + if (bitmap.pitch != (signed) bitmap.width * 4) + return; + + ft_font->lock.unlock (); + + hb_blob_t *blob = hb_blob_create ((const char *) bitmap.buffer, + bitmap.pitch * bitmap.rows, + HB_MEMORY_MODE_DUPLICATE, + nullptr, nullptr); + + hb_glyph_extents_t extents; + if (!hb_font_get_glyph_extents (font, gid, &extents)) + goto out; + + if (!paint_funcs->image (paint_data, + blob, + bitmap.width, + bitmap.rows, + HB_PAINT_IMAGE_FORMAT_BGRA, + font->slant_xy, + &extents)) + { + /* TODO Try a forced outline load and paint? */ + } + + out: + hb_blob_destroy (blob); + ft_font->lock.lock (); + } + + return; + } +} +#endif +#endif + + +static inline void free_static_ft_funcs (); + +static struct hb_ft_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_t +{ + static hb_font_funcs_t *create () + { + hb_font_funcs_t *funcs = hb_font_funcs_create (); + + hb_font_funcs_set_nominal_glyph_func (funcs, hb_ft_get_nominal_glyph, nullptr, nullptr); + hb_font_funcs_set_nominal_glyphs_func (funcs, hb_ft_get_nominal_glyphs, nullptr, nullptr); + hb_font_funcs_set_variation_glyph_func (funcs, hb_ft_get_variation_glyph, nullptr, nullptr); + + hb_font_funcs_set_font_h_extents_func (funcs, hb_ft_get_font_h_extents, nullptr, nullptr); + hb_font_funcs_set_glyph_h_advances_func (funcs, hb_ft_get_glyph_h_advances, nullptr, nullptr); + //hb_font_funcs_set_glyph_h_origin_func (funcs, hb_ft_get_glyph_h_origin, nullptr, nullptr); + +#ifndef HB_NO_VERTICAL + //hb_font_funcs_set_font_v_extents_func (funcs, hb_ft_get_font_v_extents, nullptr, nullptr); + hb_font_funcs_set_glyph_v_advance_func (funcs, hb_ft_get_glyph_v_advance, nullptr, nullptr); + hb_font_funcs_set_glyph_v_origin_func (funcs, hb_ft_get_glyph_v_origin, nullptr, nullptr); +#endif + +#ifndef HB_NO_OT_SHAPE_FALLBACK + hb_font_funcs_set_glyph_h_kerning_func (funcs, hb_ft_get_glyph_h_kerning, nullptr, nullptr); +#endif + //hb_font_funcs_set_glyph_v_kerning_func (funcs, hb_ft_get_glyph_v_kerning, nullptr, nullptr); + hb_font_funcs_set_glyph_extents_func (funcs, hb_ft_get_glyph_extents, nullptr, nullptr); + hb_font_funcs_set_glyph_contour_point_func (funcs, hb_ft_get_glyph_contour_point, nullptr, nullptr); + hb_font_funcs_set_glyph_name_func (funcs, hb_ft_get_glyph_name, nullptr, nullptr); + hb_font_funcs_set_glyph_from_name_func (funcs, hb_ft_get_glyph_from_name, nullptr, nullptr); + +#ifndef HB_NO_DRAW + hb_font_funcs_set_draw_glyph_func (funcs, hb_ft_draw_glyph, nullptr, nullptr); +#endif + +#ifndef HB_NO_PAINT +#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 21300 + hb_font_funcs_set_paint_glyph_func (funcs, hb_ft_paint_glyph, nullptr, nullptr); +#endif +#endif + + hb_font_funcs_make_immutable (funcs); + + hb_atexit (free_static_ft_funcs); + + return funcs; + } +} static_ft_funcs; + +static inline +void free_static_ft_funcs () +{ + static_ft_funcs.free_instance (); +} + +static hb_font_funcs_t * +_hb_ft_get_font_funcs () +{ + return static_ft_funcs.get_unconst (); +} + +static void +_hb_ft_font_set_funcs (hb_font_t *font, FT_Face ft_face, bool unref) +{ + bool symbol = ft_face->charmap && ft_face->charmap->encoding == FT_ENCODING_MS_SYMBOL; + + hb_ft_font_t *ft_font = _hb_ft_font_create (ft_face, symbol, unref); + if (unlikely (!ft_font)) return; + + hb_font_set_funcs (font, + _hb_ft_get_font_funcs (), + ft_font, + _hb_ft_font_destroy); +} + + +static hb_blob_t * +_hb_ft_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) +{ + FT_Face ft_face = (FT_Face) user_data; + FT_Byte *buffer; + FT_ULong length = 0; + FT_Error error; + + /* Note: FreeType like HarfBuzz uses the NONE tag for fetching the entire blob */ + + error = FT_Load_Sfnt_Table (ft_face, tag, 0, nullptr, &length); + if (error) + return nullptr; + + buffer = (FT_Byte *) hb_malloc (length); + if (!buffer) + return nullptr; + + error = FT_Load_Sfnt_Table (ft_face, tag, 0, buffer, &length); + if (error) + { + hb_free (buffer); + return nullptr; + } + + return hb_blob_create ((const char *) buffer, length, + HB_MEMORY_MODE_WRITABLE, + buffer, hb_free); +} + +/** + * hb_ft_face_create: + * @ft_face: (destroy destroy) (scope notified): FT_Face to work upon + * @destroy: (nullable): A callback to call when the face object is not needed anymore + * + * Creates an #hb_face_t face object from the specified FT_Face. + * + * Note that this is using the FT_Face object just to get at the underlying + * font data, and fonts created from the returned #hb_face_t will use the native + * HarfBuzz font implementation, unless you call hb_ft_font_set_funcs() on them. + * + * This variant of the function does not provide any life-cycle management. + * + * Most client programs should use hb_ft_face_create_referenced() + * (or, perhaps, hb_ft_face_create_cached()) instead. + * + * If you know you have valid reasons not to use hb_ft_face_create_referenced(), + * then it is the client program's responsibility to destroy @ft_face + * after the #hb_face_t face object has been destroyed. + * + * Return value: (transfer full): the new #hb_face_t face object + * + * Since: 0.9.2 + **/ +hb_face_t * +hb_ft_face_create (FT_Face ft_face, + hb_destroy_func_t destroy) +{ + hb_face_t *face; + + if (!ft_face->stream->read) { + hb_blob_t *blob; + + blob = hb_blob_create ((const char *) ft_face->stream->base, + (unsigned int) ft_face->stream->size, + HB_MEMORY_MODE_READONLY, + ft_face, destroy); + face = hb_face_create (blob, ft_face->face_index); + hb_blob_destroy (blob); + } else { + face = hb_face_create_for_tables (_hb_ft_reference_table, ft_face, destroy); + } + + hb_face_set_index (face, ft_face->face_index); + hb_face_set_upem (face, ft_face->units_per_EM); + + return face; +} + +/** + * hb_ft_face_create_referenced: + * @ft_face: FT_Face to work upon + * + * Creates an #hb_face_t face object from the specified FT_Face. + * + * Note that this is using the FT_Face object just to get at the underlying + * font data, and fonts created from the returned #hb_face_t will use the native + * HarfBuzz font implementation, unless you call hb_ft_font_set_funcs() on them. + * + * This is the preferred variant of the hb_ft_face_create* + * function family, because it calls FT_Reference_Face() on @ft_face, + * ensuring that @ft_face remains alive as long as the resulting + * #hb_face_t face object remains alive. Also calls FT_Done_Face() + * when the #hb_face_t face object is destroyed. + * + * Use this version unless you know you have good reasons not to. + * + * Return value: (transfer full): the new #hb_face_t face object + * + * Since: 0.9.38 + **/ +hb_face_t * +hb_ft_face_create_referenced (FT_Face ft_face) +{ + FT_Reference_Face (ft_face); + return hb_ft_face_create (ft_face, _hb_ft_face_destroy); +} + +static void +hb_ft_face_finalize (void *arg) +{ + FT_Face ft_face = (FT_Face) arg; + hb_face_destroy ((hb_face_t *) ft_face->generic.data); +} + +/** + * hb_ft_face_create_cached: + * @ft_face: FT_Face to work upon + * + * Creates an #hb_face_t face object from the specified FT_Face. + * + * Note that this is using the FT_Face object just to get at the underlying + * font data, and fonts created from the returned #hb_face_t will use the native + * HarfBuzz font implementation, unless you call hb_ft_font_set_funcs() on them. + * + * This variant of the function caches the newly created #hb_face_t + * face object, using the @generic pointer of @ft_face. Subsequent function + * calls that are passed the same @ft_face parameter will have the same + * #hb_face_t returned to them, and that #hb_face_t will be correctly + * reference counted. + * + * However, client programs are still responsible for destroying + * @ft_face after the last #hb_face_t face object has been destroyed. + * + * Return value: (transfer full): the new #hb_face_t face object + * + * Since: 0.9.2 + **/ +hb_face_t * +hb_ft_face_create_cached (FT_Face ft_face) +{ + if (unlikely (!ft_face->generic.data || ft_face->generic.finalizer != (FT_Generic_Finalizer) hb_ft_face_finalize)) + { + if (ft_face->generic.finalizer) + ft_face->generic.finalizer (ft_face); + + ft_face->generic.data = hb_ft_face_create (ft_face, nullptr); + ft_face->generic.finalizer = hb_ft_face_finalize; + } + + return hb_face_reference ((hb_face_t *) ft_face->generic.data); +} + +/** + * hb_ft_font_create: + * @ft_face: (destroy destroy) (scope notified): FT_Face to work upon + * @destroy: (nullable): A callback to call when the font object is not needed anymore + * + * Creates an #hb_font_t font object from the specified FT_Face. + * + * Note: You must set the face size on @ft_face before calling + * hb_ft_font_create() on it. HarfBuzz assumes size is always set and will + * access `size` member of FT_Face unconditionally. + * + * This variant of the function does not provide any life-cycle management. + * + * Most client programs should use hb_ft_font_create_referenced() + * instead. + * + * If you know you have valid reasons not to use hb_ft_font_create_referenced(), + * then it is the client program's responsibility to destroy @ft_face + * after the #hb_font_t font object has been destroyed. + * + * HarfBuzz will use the @destroy callback on the #hb_font_t font object + * if it is supplied when you use this function. However, even if @destroy + * is provided, it is the client program's responsibility to destroy @ft_face, + * and it is the client program's responsibility to ensure that @ft_face is + * destroyed only after the #hb_font_t font object has been destroyed. + * + * Return value: (transfer full): the new #hb_font_t font object + * + * Since: 0.9.2 + **/ +hb_font_t * +hb_ft_font_create (FT_Face ft_face, + hb_destroy_func_t destroy) +{ + hb_font_t *font; + hb_face_t *face; + + face = hb_ft_face_create (ft_face, destroy); + font = hb_font_create (face); + hb_face_destroy (face); + _hb_ft_font_set_funcs (font, ft_face, false); + hb_ft_font_changed (font); + return font; +} + +/** + * hb_ft_font_changed: + * @font: #hb_font_t to work upon + * + * Refreshes the state of @font when the underlying FT_Face has changed. + * This function should be called after changing the size or + * variation-axis settings on the FT_Face. + * + * Since: 1.0.5 + **/ +void +hb_ft_font_changed (hb_font_t *font) +{ + if (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy) + return; + + hb_ft_font_t *ft_font = (hb_ft_font_t *) font->user_data; + + FT_Face ft_face = ft_font->ft_face; + + hb_font_set_scale (font, + (int) (((uint64_t) ft_face->size->metrics.x_scale * (uint64_t) ft_face->units_per_EM + (1u<<15)) >> 16), + (int) (((uint64_t) ft_face->size->metrics.y_scale * (uint64_t) ft_face->units_per_EM + (1u<<15)) >> 16)); +#if 0 /* hb-ft works in no-hinting model */ + hb_font_set_ppem (font, + ft_face->size->metrics.x_ppem, + ft_face->size->metrics.y_ppem); +#endif + +#if defined(HAVE_FT_GET_VAR_BLEND_COORDINATES) && !defined(HB_NO_VAR) + FT_MM_Var *mm_var = nullptr; + if (!FT_Get_MM_Var (ft_face, &mm_var)) + { + FT_Fixed *ft_coords = (FT_Fixed *) hb_calloc (mm_var->num_axis, sizeof (FT_Fixed)); + int *coords = (int *) hb_calloc (mm_var->num_axis, sizeof (int)); + if (coords && ft_coords) + { + if (!FT_Get_Var_Blend_Coordinates (ft_face, mm_var->num_axis, ft_coords)) + { + bool nonzero = false; + + for (unsigned int i = 0; i < mm_var->num_axis; ++i) + { + coords[i] = ft_coords[i] >>= 2; + nonzero = nonzero || coords[i]; + } + + if (nonzero) + hb_font_set_var_coords_normalized (font, coords, mm_var->num_axis); + else + hb_font_set_var_coords_normalized (font, nullptr, 0); + } + } + hb_free (coords); + hb_free (ft_coords); +#ifdef HAVE_FT_DONE_MM_VAR + FT_Done_MM_Var (ft_face->glyph->library, mm_var); +#else + hb_free (mm_var); +#endif + } +#endif + + ft_font->advance_cache.clear (); + ft_font->cached_serial = font->serial; +} + +/** + * hb_ft_hb_font_changed: + * @font: #hb_font_t to work upon + * + * Refreshes the state of the underlying FT_Face of @font when the hb_font_t + * @font has changed. + * This function should be called after changing the size or + * variation-axis settings on the @font. + * This call is fast if nothing has changed on @font. + * + * Return value: true if changed, false otherwise + * + * Since: 4.4.0 + **/ +hb_bool_t +hb_ft_hb_font_changed (hb_font_t *font) +{ + if (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy) + return false; + + hb_ft_font_t *ft_font = (hb_ft_font_t *) font->user_data; + + return _hb_ft_hb_font_check_changed (font, ft_font); +} + +/** + * hb_ft_font_create_referenced: + * @ft_face: FT_Face to work upon + * + * Creates an #hb_font_t font object from the specified FT_Face. + * + * Note: You must set the face size on @ft_face before calling + * hb_ft_font_create_referenced() on it. HarfBuzz assumes size is always set + * and will access `size` member of FT_Face unconditionally. + * + * This is the preferred variant of the hb_ft_font_create* + * function family, because it calls FT_Reference_Face() on @ft_face, + * ensuring that @ft_face remains alive as long as the resulting + * #hb_font_t font object remains alive. + * + * Use this version unless you know you have good reasons not to. + * + * Return value: (transfer full): the new #hb_font_t font object + * + * Since: 0.9.38 + **/ +hb_font_t * +hb_ft_font_create_referenced (FT_Face ft_face) +{ + FT_Reference_Face (ft_face); + return hb_ft_font_create (ft_face, _hb_ft_face_destroy); +} + +static inline void free_static_ft_library (); + +static struct hb_ft_library_lazy_loader_t : hb_lazy_loader_t, + hb_ft_library_lazy_loader_t> +{ + static FT_Library create () + { + FT_Library l; + if (FT_Init_FreeType (&l)) + return nullptr; + + hb_atexit (free_static_ft_library); + + return l; + } + static void destroy (FT_Library l) + { + FT_Done_FreeType (l); + } + static FT_Library get_null () + { + return nullptr; + } +} static_ft_library; + +static inline +void free_static_ft_library () +{ + static_ft_library.free_instance (); +} + +static FT_Library +get_ft_library () +{ + return static_ft_library.get_unconst (); +} + +static void +_release_blob (void *arg) +{ + FT_Face ft_face = (FT_Face) arg; + hb_blob_destroy ((hb_blob_t *) ft_face->generic.data); +} + +/** + * hb_ft_font_set_funcs: + * @font: #hb_font_t to work upon + * + * Configures the font-functions structure of the specified + * #hb_font_t font object to use FreeType font functions. + * + * In particular, you can use this function to configure an + * existing #hb_face_t face object for use with FreeType font + * functions even if that #hb_face_t face object was initially + * created with hb_face_create(), and therefore was not + * initially configured to use FreeType font functions. + * + * An #hb_font_t object created with hb_ft_font_create() + * is preconfigured for FreeType font functions and does not + * require this function to be used. + * + * Note that if you modify the underlying #hb_font_t after + * calling this function, you need to call hb_ft_hb_font_changed() + * to update the underlying FT_Face. + * + * Note: Internally, this function creates an FT_Face. +* + * + * Since: 1.0.5 + **/ +void +hb_ft_font_set_funcs (hb_font_t *font) +{ + hb_blob_t *blob = hb_face_reference_blob (font->face); + unsigned int blob_length; + const char *blob_data = hb_blob_get_data (blob, &blob_length); + if (unlikely (!blob_length)) + DEBUG_MSG (FT, font, "Font face has empty blob"); + + FT_Face ft_face = nullptr; + FT_Error err = FT_New_Memory_Face (get_ft_library (), + (const FT_Byte *) blob_data, + blob_length, + hb_face_get_index (font->face), + &ft_face); + + if (unlikely (err)) { + hb_blob_destroy (blob); + DEBUG_MSG (FT, font, "Font face FT_New_Memory_Face() failed"); + return; + } + + if (FT_Select_Charmap (ft_face, FT_ENCODING_MS_SYMBOL)) + FT_Select_Charmap (ft_face, FT_ENCODING_UNICODE); + + + ft_face->generic.data = blob; + ft_face->generic.finalizer = _release_blob; + + _hb_ft_font_set_funcs (font, ft_face, true); + hb_ft_font_set_load_flags (font, FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING); + + _hb_ft_hb_font_changed (font, ft_face); +} + +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ft.h b/src/3rdparty/harfbuzz-ng/src/hb-ft.h new file mode 100644 index 00000000000..6a8a7abe8cc --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-ft.h @@ -0,0 +1,145 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2015 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_FT_H +#define HB_FT_H + +#include "hb.h" + +#include +#include FT_FREETYPE_H + +HB_BEGIN_DECLS + +/* + * Note: FreeType is not thread-safe. + * Hence, these functions are not either. + */ + +/* + * hb-face from ft-face. + */ + +/* This one creates a new hb-face for given ft-face. + * When the returned hb-face is destroyed, the destroy + * callback is called (if not NULL), with the ft-face passed + * to it. + * + * The client is responsible to make sure that ft-face is + * destroyed after hb-face is destroyed. + * + * Most often you don't want this function. You should use either + * hb_ft_face_create_cached(), or hb_ft_face_create_referenced(). + * In particular, if you are going to pass NULL as destroy, you + * probably should use (the more recent) hb_ft_face_create_referenced() + * instead. + */ +HB_EXTERN hb_face_t * +hb_ft_face_create (FT_Face ft_face, + hb_destroy_func_t destroy); + +/* This version is like hb_ft_face_create(), except that it caches + * the hb-face using the generic pointer of the ft-face. This means + * that subsequent calls to this function with the same ft-face will + * return the same hb-face (correctly referenced). + * + * Client is still responsible for making sure that ft-face is destroyed + * after hb-face is. + */ +HB_EXTERN hb_face_t * +hb_ft_face_create_cached (FT_Face ft_face); + +/* This version is like hb_ft_face_create(), except that it calls + * FT_Reference_Face() on ft-face, as such keeping ft-face alive + * as long as the hb-face is. + * + * This is the most convenient version to use. Use it unless you have + * very good reasons not to. + */ +HB_EXTERN hb_face_t * +hb_ft_face_create_referenced (FT_Face ft_face); + + +/* + * hb-font from ft-face. + */ + +/* + * Note: + * + * Set face size on ft-face before creating hb-font from it. + * Otherwise hb-ft would NOT pick up the font size correctly. + */ + +/* See notes on hb_ft_face_create(). Same issues re lifecycle-management + * apply here. Use hb_ft_font_create_referenced() if you can. */ +HB_EXTERN hb_font_t * +hb_ft_font_create (FT_Face ft_face, + hb_destroy_func_t destroy); + +/* See notes on hb_ft_face_create_referenced() re lifecycle-management + * issues. */ +HB_EXTERN hb_font_t * +hb_ft_font_create_referenced (FT_Face ft_face); + +HB_EXTERN FT_Face +hb_ft_font_get_face (hb_font_t *font); + +HB_EXTERN FT_Face +hb_ft_font_lock_face (hb_font_t *font); + +HB_EXTERN void +hb_ft_font_unlock_face (hb_font_t *font); + +HB_EXTERN void +hb_ft_font_set_load_flags (hb_font_t *font, int load_flags); + +HB_EXTERN int +hb_ft_font_get_load_flags (hb_font_t *font); + +/* Call when size or variations settings on underlying FT_Face changed, + * and you want to update the hb_font_t from it. */ +HB_EXTERN void +hb_ft_font_changed (hb_font_t *font); + +/* Call when size or variations settings on underlying hb_font_t may have + * changed, and you want to update the FT_Face from it. This call is fast + * if nothing changed on hb_font_t. Returns true if changed. */ +HB_EXTERN hb_bool_t +hb_ft_hb_font_changed (hb_font_t *font); + +/* Makes an hb_font_t use FreeType internally to implement font functions. + * Note: this internally creates an FT_Face. Use it when you create your + * hb_face_t using hb_face_create(). */ +HB_EXTERN void +hb_ft_font_set_funcs (hb_font_t *font); + + +HB_END_DECLS + +#endif /* HB_FT_H */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-gdi.cc b/src/3rdparty/harfbuzz-ng/src/hb-gdi.cc new file mode 100644 index 00000000000..8e7589beacb --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-gdi.cc @@ -0,0 +1,85 @@ +/* + * Copyright © 2019 Ebrahim Byagowi + * + * 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. + */ + +#include "hb.hh" + +#ifdef HAVE_GDI + +#include "hb-gdi.h" + + +/** + * SECTION:hb-gdi + * @title: hb-gdi + * @short_description: GDI integration + * @include: hb-gdi.h + * + * Functions for using HarfBuzz with GDI fonts. + **/ + +static hb_blob_t * +_hb_gdi_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) +{ + char *buffer = nullptr; + DWORD length = 0; + + HDC hdc = GetDC (nullptr); + if (unlikely (!SelectObject (hdc, (HFONT) user_data))) goto fail; + + length = GetFontData (hdc, hb_uint32_swap (tag), 0, buffer, length); + if (unlikely (length == GDI_ERROR)) goto fail_with_releasedc; + + buffer = (char *) hb_malloc (length); + if (unlikely (!buffer)) goto fail_with_releasedc; + length = GetFontData (hdc, hb_uint32_swap (tag), 0, buffer, length); + if (unlikely (length == GDI_ERROR)) goto fail_with_releasedc_and_free; + ReleaseDC (nullptr, hdc); + + return hb_blob_create ((const char *) buffer, length, HB_MEMORY_MODE_WRITABLE, buffer, hb_free); + +fail_with_releasedc_and_free: + hb_free (buffer); +fail_with_releasedc: + ReleaseDC (nullptr, hdc); +fail: + return hb_blob_get_empty (); +} + +/** + * hb_gdi_face_create: + * @hfont: a HFONT object. + * + * Constructs a new face object from the specified GDI HFONT. + * + * Return value: #hb_face_t object corresponding to the given input + * + * Since: 2.6.0 + **/ +hb_face_t * +hb_gdi_face_create (HFONT hfont) +{ + return hb_face_create_for_tables (_hb_gdi_reference_table, (void *) hfont, nullptr); +} + +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-gdi.h b/src/3rdparty/harfbuzz-ng/src/hb-gdi.h new file mode 100644 index 00000000000..68cc43917e0 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-gdi.h @@ -0,0 +1,39 @@ +/* + * Copyright © 2019 Ebrahim Byagowi + * + * 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. + */ + +#ifndef HB_GDI_H +#define HB_GDI_H + +#include "hb.h" + +#include + +HB_BEGIN_DECLS + +HB_EXTERN hb_face_t * +hb_gdi_face_create (HFONT hfont); + +HB_END_DECLS + +#endif /* HB_GDI_H */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-glib.cc b/src/3rdparty/harfbuzz-ng/src/hb-glib.cc new file mode 100644 index 00000000000..1da81696e75 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-glib.cc @@ -0,0 +1,232 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2011 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#ifdef HAVE_GLIB + +#include "hb-glib.h" + +#include "hb-machinery.hh" + + +/** + * SECTION:hb-glib + * @title: hb-glib + * @short_description: GLib integration + * @include: hb-glib.h + * + * Functions for using HarfBuzz with the GLib library. + * + * HarfBuzz supports using GLib to provide Unicode data, by attaching + * GLib functions to the virtual methods in a #hb_unicode_funcs_t function + * structure. + **/ + + +/** + * hb_glib_script_to_script: + * @script: The GUnicodeScript identifier to query + * + * Fetches the #hb_script_t script that corresponds to the + * specified GUnicodeScript identifier. + * + * Return value: the #hb_script_t script found + * + * Since: 0.9.38 + **/ +hb_script_t +hb_glib_script_to_script (GUnicodeScript script) +{ + return (hb_script_t) g_unicode_script_to_iso15924 (script); +} + +/** + * hb_glib_script_from_script: + * @script: The #hb_script_t to query + * + * Fetches the GUnicodeScript identifier that corresponds to the + * specified #hb_script_t script. + * + * Return value: the GUnicodeScript identifier found + * + * Since: 0.9.38 + **/ +GUnicodeScript +hb_glib_script_from_script (hb_script_t script) +{ + return g_unicode_script_from_iso15924 (script); +} + + +static hb_unicode_combining_class_t +hb_glib_unicode_combining_class (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) + +{ + return (hb_unicode_combining_class_t) g_unichar_combining_class (unicode); +} + +static hb_unicode_general_category_t +hb_glib_unicode_general_category (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) + +{ + /* hb_unicode_general_category_t and GUnicodeType are identical */ + return (hb_unicode_general_category_t) g_unichar_type (unicode); +} + +static hb_codepoint_t +hb_glib_unicode_mirroring (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + g_unichar_get_mirror_char (unicode, &unicode); + return unicode; +} + +static hb_script_t +hb_glib_unicode_script (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + return hb_glib_script_to_script (g_unichar_get_script (unicode)); +} + +static hb_bool_t +hb_glib_unicode_compose (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab, + void *user_data HB_UNUSED) +{ +#if GLIB_CHECK_VERSION(2,29,12) + return g_unichar_compose (a, b, ab); +#else + return false; +#endif +} + +static hb_bool_t +hb_glib_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t ab, + hb_codepoint_t *a, + hb_codepoint_t *b, + void *user_data HB_UNUSED) +{ +#if GLIB_CHECK_VERSION(2,29,12) + return g_unichar_decompose (ab, a, b); +#else + return false; +#endif +} + + +static inline void free_static_glib_funcs (); + +static struct hb_glib_unicode_funcs_lazy_loader_t : hb_unicode_funcs_lazy_loader_t +{ + static hb_unicode_funcs_t *create () + { + hb_unicode_funcs_t *funcs = hb_unicode_funcs_create (nullptr); + + hb_unicode_funcs_set_combining_class_func (funcs, hb_glib_unicode_combining_class, nullptr, nullptr); + hb_unicode_funcs_set_general_category_func (funcs, hb_glib_unicode_general_category, nullptr, nullptr); + hb_unicode_funcs_set_mirroring_func (funcs, hb_glib_unicode_mirroring, nullptr, nullptr); + hb_unicode_funcs_set_script_func (funcs, hb_glib_unicode_script, nullptr, nullptr); + hb_unicode_funcs_set_compose_func (funcs, hb_glib_unicode_compose, nullptr, nullptr); + hb_unicode_funcs_set_decompose_func (funcs, hb_glib_unicode_decompose, nullptr, nullptr); + + hb_unicode_funcs_make_immutable (funcs); + + hb_atexit (free_static_glib_funcs); + + return funcs; + } +} static_glib_funcs; + +static inline +void free_static_glib_funcs () +{ + static_glib_funcs.free_instance (); +} + +/** + * hb_glib_get_unicode_funcs: + * + * Fetches a Unicode-functions structure that is populated + * with the appropriate GLib function for each method. + * + * Return value: (transfer none): a pointer to the #hb_unicode_funcs_t Unicode-functions structure + * + * Since: 0.9.38 + **/ +hb_unicode_funcs_t * +hb_glib_get_unicode_funcs () +{ + return static_glib_funcs.get_unconst (); +} + + + +#if GLIB_CHECK_VERSION(2,31,10) + +static void +_hb_g_bytes_unref (void *data) +{ + g_bytes_unref ((GBytes *) data); +} + +/** + * hb_glib_blob_create: + * @gbytes: the GBytes structure to work upon + * + * Creates an #hb_blob_t blob from the specified + * GBytes data structure. + * + * Return value: (transfer full): the new #hb_blob_t blob object + * + * Since: 0.9.38 + **/ +hb_blob_t * +hb_glib_blob_create (GBytes *gbytes) +{ + gsize size = 0; + gconstpointer data = g_bytes_get_data (gbytes, &size); + return hb_blob_create ((const char *) data, + size, + HB_MEMORY_MODE_READONLY, + g_bytes_ref (gbytes), + _hb_g_bytes_unref); +} +#endif + + +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-tag.h b/src/3rdparty/harfbuzz-ng/src/hb-glib.h similarity index 69% rename from src/3rdparty/harfbuzz-ng/src/hb-ot-tag.h rename to src/3rdparty/harfbuzz-ng/src/hb-glib.h index 54fb747f582..5f04183ba19 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-ot-tag.h +++ b/src/3rdparty/harfbuzz-ng/src/hb-glib.h @@ -1,5 +1,6 @@ /* * Copyright © 2009 Red Hat, Inc. + * Copyright © 2011 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -22,38 +23,34 @@ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. * * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod */ -#ifndef HB_OT_H_IN -#error "Include instead." -#endif - -#ifndef HB_OT_TAG_H -#define HB_OT_TAG_H +#ifndef HB_GLIB_H +#define HB_GLIB_H #include "hb.h" +#include + HB_BEGIN_DECLS -#define HB_OT_TAG_DEFAULT_SCRIPT HB_TAG ('D', 'F', 'L', 'T') -#define HB_OT_TAG_DEFAULT_LANGUAGE HB_TAG ('d', 'f', 'l', 't') - -HB_EXTERN void -hb_ot_tags_from_script (hb_script_t script, - hb_tag_t *script_tag_1, - hb_tag_t *script_tag_2); - HB_EXTERN hb_script_t -hb_ot_tag_to_script (hb_tag_t tag); +hb_glib_script_to_script (GUnicodeScript script); -HB_EXTERN hb_tag_t -hb_ot_tag_from_language (hb_language_t language); +HB_EXTERN GUnicodeScript +hb_glib_script_from_script (hb_script_t script); -HB_EXTERN hb_language_t -hb_ot_tag_to_language (hb_tag_t tag); +HB_EXTERN hb_unicode_funcs_t * +hb_glib_get_unicode_funcs (void); + +#if GLIB_CHECK_VERSION(2,31,10) +HB_EXTERN hb_blob_t * +hb_glib_blob_create (GBytes *gbytes); +#endif HB_END_DECLS -#endif /* HB_OT_TAG_H */ +#endif /* HB_GLIB_H */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-shape-plan-private.hh b/src/3rdparty/harfbuzz-ng/src/hb-gobject-enums.cc.tmpl similarity index 50% rename from src/3rdparty/harfbuzz-ng/src/hb-shape-plan-private.hh rename to src/3rdparty/harfbuzz-ng/src/hb-gobject-enums.cc.tmpl index aa0413a272d..87a11dd407e 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-shape-plan-private.hh +++ b/src/3rdparty/harfbuzz-ng/src/hb-gobject-enums.cc.tmpl @@ -1,5 +1,6 @@ +/*** BEGIN file-header ***/ /* - * Copyright © 2012 Google, Inc. + * Copyright (C) 2011 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -24,44 +25,56 @@ * Google Author(s): Behdad Esfahbod */ -#ifndef HB_SHAPE_PLAN_PRIVATE_HH -#define HB_SHAPE_PLAN_PRIVATE_HH +#include "hb.hh" -#include "hb-private.hh" -#include "hb-object-private.hh" -#include "hb-shaper-private.hh" +#ifdef HAVE_GOBJECT +/* g++ didn't like older gtype.h gcc-only code path. */ +#include +#if !GLIB_CHECK_VERSION(2,29,16) +#undef __GNUC__ +#undef __GNUC_MINOR__ +#define __GNUC__ 2 +#define __GNUC_MINOR__ 6 +#endif -struct hb_shape_plan_t +#include "hb-gobject.h" + +/*** END file-header ***/ + +/*** BEGIN file-production ***/ +/* enumerations from "@basename@" */ +/*** END file-production ***/ + +/*** BEGIN file-tail ***/ + +#endif +/*** END file-tail ***/ + +/*** BEGIN value-header ***/ +GType +@enum_name@_get_type () { - hb_object_header_t header; - ASSERT_POD (); + static gsize type_id = 0; - hb_bool_t default_shaper_list; - hb_face_t *face_unsafe; /* We don't carry a reference to face. */ - hb_segment_properties_t props; + if (g_once_init_enter (&type_id)) + { + static const G@Type@Value values[] = { +/*** END value-header ***/ - hb_shape_func_t *shaper_func; - const char *shaper_name; +/*** BEGIN value-production ***/ + { @VALUENAME@, "@VALUENAME@", "@valuenick@" }, +/*** END value-production ***/ - hb_feature_t *user_features; - unsigned int num_user_features; +/*** BEGIN value-tail ***/ + { 0, NULL, NULL } + }; + GType id = + g_@type@_register_static (g_intern_static_string ("@EnumName@"), values); + g_once_init_leave (&type_id, id); + } - int *coords; - unsigned int num_coords; + return type_id; +} - struct hb_shaper_data_t shaper_data; -}; - -#define HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS \ - , const hb_feature_t *user_features \ - , unsigned int num_user_features \ - , const int *coords \ - , unsigned int num_coords -#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_PROTOTYPE(shaper, shape_plan); -#include "hb-shaper-list.hh" -#undef HB_SHAPER_IMPLEMENT -#undef HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS - - -#endif /* HB_SHAPE_PLAN_PRIVATE_HH */ +/*** END value-tail ***/ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-gobject-enums.h.tmpl b/src/3rdparty/harfbuzz-ng/src/hb-gobject-enums.h.tmpl new file mode 100644 index 00000000000..6dd98f7fed6 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-gobject-enums.h.tmpl @@ -0,0 +1,56 @@ +/*** BEGIN file-header ***/ +/* + * Copyright (C) 2013 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): Behdad Esfahbod + */ + +#if !defined(HB_GOBJECT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_GOBJECT_ENUMS_H +#define HB_GOBJECT_ENUMS_H + +#include "hb.h" + +#include + +HB_BEGIN_DECLS + + +/*** END file-header ***/ + +/*** BEGIN value-header ***/ +HB_EXTERN GType +@enum_name@_get_type (void) G_GNUC_CONST; +#define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type ()) + +/*** END value-header ***/ + +/*** BEGIN file-tail ***/ + +HB_END_DECLS + +#endif /* HB_GOBJECT_ENUMS_H */ +/*** END file-tail ***/ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-gobject-structs.cc b/src/3rdparty/harfbuzz-ng/src/hb-gobject-structs.cc new file mode 100644 index 00000000000..d66de0b237f --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-gobject-structs.cc @@ -0,0 +1,116 @@ +/* + * Copyright © 2011 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): Behdad Esfahbod + */ + +#include "hb.hh" + +#ifdef HAVE_GOBJECT + + +/* + * SECTION:hb-gobject + * @title: hb-gobject + * @short_description: GObject integration support + * @include: hb-gobject.h + * + * Support for using HarfBuzz with the GObject library to provide + * type data. + * + * The types and functions listed here are solely a linkage between + * HarfBuzz's public data types and the GTypes used by the GObject framework. + * HarfBuzz uses GObject introspection to generate its Python bindings + * (and potentially other language bindings); client programs should never need + * to access the GObject-integration mechanics. + * + * For client programs using the GNOME and GTK software stack, please see the + * GLib and FreeType integration pages. + **/ + + +/* g++ didn't like older gtype.h gcc-only code path. */ +#include +#if !GLIB_CHECK_VERSION(2,29,16) +#undef __GNUC__ +#undef __GNUC_MINOR__ +#define __GNUC__ 2 +#define __GNUC_MINOR__ 6 +#endif + +#include "hb-gobject.h" + +#define HB_DEFINE_BOXED_TYPE(name,copy_func,free_func) \ +GType \ +hb_gobject_##name##_get_type () \ +{ \ + static gsize type_id = 0; \ + if (g_once_init_enter (&type_id)) { \ + GType id = g_boxed_type_register_static (g_intern_static_string ("hb_" #name "_t"), \ + (GBoxedCopyFunc) copy_func, \ + (GBoxedFreeFunc) free_func); \ + g_once_init_leave (&type_id, id); \ + } \ + return type_id; \ +} + +#define HB_DEFINE_OBJECT_TYPE(name) \ + HB_DEFINE_BOXED_TYPE (name, hb_##name##_reference, hb_##name##_destroy) + +#define HB_DEFINE_VALUE_TYPE(name) \ + static hb_##name##_t *_hb_##name##_reference (const hb_##name##_t *l) \ + { \ + hb_##name##_t *c = (hb_##name##_t *) hb_calloc (1, sizeof (hb_##name##_t)); \ + if (unlikely (!c)) return nullptr; \ + *c = *l; \ + return c; \ + } \ + static void _hb_##name##_destroy (hb_##name##_t *l) { hb_free (l); } \ + HB_DEFINE_BOXED_TYPE (name, _hb_##name##_reference, _hb_##name##_destroy) + +HB_DEFINE_OBJECT_TYPE (buffer) +HB_DEFINE_OBJECT_TYPE (blob) +HB_DEFINE_OBJECT_TYPE (draw_funcs) +HB_DEFINE_OBJECT_TYPE (paint_funcs) +HB_DEFINE_OBJECT_TYPE (face) +HB_DEFINE_OBJECT_TYPE (font) +HB_DEFINE_OBJECT_TYPE (font_funcs) +HB_DEFINE_OBJECT_TYPE (set) +HB_DEFINE_OBJECT_TYPE (map) +HB_DEFINE_OBJECT_TYPE (shape_plan) +HB_DEFINE_OBJECT_TYPE (unicode_funcs) +HB_DEFINE_VALUE_TYPE (feature) +HB_DEFINE_VALUE_TYPE (glyph_info) +HB_DEFINE_VALUE_TYPE (glyph_position) +HB_DEFINE_VALUE_TYPE (segment_properties) +HB_DEFINE_VALUE_TYPE (draw_state) +HB_DEFINE_VALUE_TYPE (color_stop) +HB_DEFINE_VALUE_TYPE (color_line) +HB_DEFINE_VALUE_TYPE (user_data_key) + +HB_DEFINE_VALUE_TYPE (ot_var_axis_info) +HB_DEFINE_VALUE_TYPE (ot_math_glyph_variant) +HB_DEFINE_VALUE_TYPE (ot_math_glyph_part) + + +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-gobject-structs.h b/src/3rdparty/harfbuzz-ng/src/hb-gobject-structs.h new file mode 100644 index 00000000000..b7b5f55ce67 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-gobject-structs.h @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2011 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): Behdad Esfahbod + */ + +#if !defined(HB_GOBJECT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_GOBJECT_STRUCTS_H +#define HB_GOBJECT_STRUCTS_H + +#include "hb.h" + +#include + +HB_BEGIN_DECLS + + +/* Object types */ + +HB_EXTERN GType +hb_gobject_blob_get_type (void); +#define HB_GOBJECT_TYPE_BLOB (hb_gobject_blob_get_type ()) + +HB_EXTERN GType +hb_gobject_buffer_get_type (void); +#define HB_GOBJECT_TYPE_BUFFER (hb_gobject_buffer_get_type ()) + +HB_EXTERN GType +hb_gobject_draw_funcs_get_type (void); +#define HB_GOBJECT_TYPE_DRAW_FUNCS (hb_gobject_draw_funcs_get_type ()) + +HB_EXTERN GType +hb_gobject_paint_funcs_get_type (void); +#define HB_GOBJECT_TYPE_PAINT_FUNCS (hb_gobject_paint_funcs_get_type ()) + +HB_EXTERN GType +hb_gobject_face_get_type (void); +#define HB_GOBJECT_TYPE_FACE (hb_gobject_face_get_type ()) + +HB_EXTERN GType +hb_gobject_font_get_type (void); +#define HB_GOBJECT_TYPE_FONT (hb_gobject_font_get_type ()) + +HB_EXTERN GType +hb_gobject_font_funcs_get_type (void); +#define HB_GOBJECT_TYPE_FONT_FUNCS (hb_gobject_font_funcs_get_type ()) + +HB_EXTERN GType +hb_gobject_set_get_type (void); +#define HB_GOBJECT_TYPE_SET (hb_gobject_set_get_type ()) + +HB_EXTERN GType +hb_gobject_map_get_type (void); +#define HB_GOBJECT_TYPE_MAP (hb_gobject_map_get_type ()) + +HB_EXTERN GType +hb_gobject_shape_plan_get_type (void); +#define HB_GOBJECT_TYPE_SHAPE_PLAN (hb_gobject_shape_plan_get_type ()) + +HB_EXTERN GType +hb_gobject_unicode_funcs_get_type (void); +#define HB_GOBJECT_TYPE_UNICODE_FUNCS (hb_gobject_unicode_funcs_get_type ()) + +/* Value types */ + +HB_EXTERN GType +hb_gobject_feature_get_type (void); +#define HB_GOBJECT_TYPE_FEATURE (hb_gobject_feature_get_type ()) + +HB_EXTERN GType +hb_gobject_glyph_info_get_type (void); +#define HB_GOBJECT_TYPE_GLYPH_INFO (hb_gobject_glyph_info_get_type ()) + +HB_EXTERN GType +hb_gobject_glyph_position_get_type (void); +#define HB_GOBJECT_TYPE_GLYPH_POSITION (hb_gobject_glyph_position_get_type ()) + +HB_EXTERN GType +hb_gobject_segment_properties_get_type (void); +#define HB_GOBJECT_TYPE_SEGMENT_PROPERTIES (hb_gobject_segment_properties_get_type ()) + +HB_EXTERN GType +hb_gobject_draw_state_get_type (void); +#define HB_GOBJECT_TYPE_DRAW_STATE (hb_gobject_draw_state_get_type ()) + +HB_EXTERN GType +hb_gobject_color_stop_get_type (void); +#define HB_GOBJECT_TYPE_COLOR_STOP (hb_gobject_color_stop_get_type ()) + +HB_EXTERN GType +hb_gobject_color_line_get_type (void); +#define HB_GOBJECT_TYPE_COLOR_LINE (hb_gobject_color_line_get_type ()) + +HB_EXTERN GType +hb_gobject_user_data_key_get_type (void); +#define HB_GOBJECT_TYPE_USER_DATA_KEY (hb_gobject_user_data_key_get_type ()) + +HB_EXTERN GType +hb_gobject_ot_var_axis_info_get_type (void); +#define HB_GOBJECT_TYPE_OT_VAR_AXIS_INFO (hb_gobject_ot_var_axis_info_get_type ()) + +HB_EXTERN GType +hb_gobject_ot_math_glyph_variant_get_type (void); +#define HB_GOBJECT_TYPE_OT_MATH_GLYPH_VARIANT (hb_gobject_ot_math_glyph_variant_get_type ()) + +HB_EXTERN GType +hb_gobject_ot_math_glyph_part_get_type (void); +#define HB_GOBJECT_TYPE_OT_MATH_GLYPH_PART (hb_gobject_ot_math_glyph_part_get_type ()) + + +HB_END_DECLS + +#endif /* HB_GOBJECT_H */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-warning.cc b/src/3rdparty/harfbuzz-ng/src/hb-gobject.h similarity index 68% rename from src/3rdparty/harfbuzz-ng/src/hb-warning.cc rename to src/3rdparty/harfbuzz-ng/src/hb-gobject.h index 8f322bcb10d..8891aa0ee79 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-warning.cc +++ b/src/3rdparty/harfbuzz-ng/src/hb-gobject.h @@ -1,5 +1,5 @@ /* - * Copyright © 2012 Google, Inc. + * Copyright (C) 2011 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -24,16 +24,17 @@ * Google Author(s): Behdad Esfahbod */ -#include "hb-atomic-private.hh" -#include "hb-mutex-private.hh" +#ifndef HB_GOBJECT_H +#define HB_GOBJECT_H +#define HB_GOBJECT_H_IN +#include "hb.h" -#if defined(HB_ATOMIC_INT_NIL) -#error "Could not find any system to define atomic_int macros, library WILL NOT be thread-safe" -#error "Check hb-atomic-private.hh for possible resolutions." -#endif +#include "hb-gobject-enums.h" +#include "hb-gobject-structs.h" -#if defined(HB_MUTEX_IMPL_NIL) -#error "Could not find any system to define mutex macros, library WILL NOT be thread-safe" -#error "Check hb-mutex-private.hh for possible resolutions." -#endif +HB_BEGIN_DECLS +HB_END_DECLS + +#undef HB_GOBJECT_H_IN +#endif /* HB_GOBJECT_H */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-graphite2.cc b/src/3rdparty/harfbuzz-ng/src/hb-graphite2.cc new file mode 100644 index 00000000000..7ea03862238 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-graphite2.cc @@ -0,0 +1,455 @@ +/* + * Copyright © 2011 Martin Hosken + * Copyright © 2011 SIL International + * Copyright © 2011,2012 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): Behdad Esfahbod + */ + +#include "hb.hh" + +#ifdef HAVE_GRAPHITE2 + +#include "hb-shaper-impl.hh" + +#include "hb-graphite2.h" + +#include + +#include "hb-ot-layout.h" + + +/** + * SECTION:hb-graphite2 + * @title: hb-graphite2 + * @short_description: Graphite2 integration + * @include: hb-graphite2.h + * + * Functions for using HarfBuzz with fonts that include Graphite features. + * + * For Graphite features to work, you must be sure that HarfBuzz was compiled + * with the `graphite2` shaping engine enabled. Currently, the default is to + * not enable `graphite2` shaping. + **/ + + +/* + * shaper face data + */ + +typedef struct hb_graphite2_tablelist_t +{ + struct hb_graphite2_tablelist_t *next; + hb_blob_t *blob; + unsigned int tag; +} hb_graphite2_tablelist_t; + +struct hb_graphite2_face_data_t +{ + hb_face_t *face; + gr_face *grface; + hb_atomic_ptr_t tlist; +}; + +static const void *hb_graphite2_get_table (const void *data, unsigned int tag, size_t *len) +{ + hb_graphite2_face_data_t *face_data = (hb_graphite2_face_data_t *) data; + hb_graphite2_tablelist_t *tlist = face_data->tlist; + + hb_blob_t *blob = nullptr; + + for (hb_graphite2_tablelist_t *p = tlist; p; p = p->next) + if (p->tag == tag) { + blob = p->blob; + break; + } + + if (unlikely (!blob)) + { + blob = face_data->face->reference_table (tag); + + hb_graphite2_tablelist_t *p = (hb_graphite2_tablelist_t *) hb_calloc (1, sizeof (hb_graphite2_tablelist_t)); + if (unlikely (!p)) { + hb_blob_destroy (blob); + return nullptr; + } + p->blob = blob; + p->tag = tag; + +retry: + hb_graphite2_tablelist_t *tlist = face_data->tlist; + p->next = tlist; + + if (unlikely (!face_data->tlist.cmpexch (tlist, p))) + goto retry; + } + + unsigned int tlen; + const char *d = hb_blob_get_data (blob, &tlen); + *len = tlen; + return d; +} + +hb_graphite2_face_data_t * +_hb_graphite2_shaper_face_data_create (hb_face_t *face) +{ + hb_blob_t *silf_blob = face->reference_table (HB_GRAPHITE2_TAG_SILF); + /* Umm, we just reference the table to check whether it exists. + * Maybe add better API for this? */ + if (!hb_blob_get_length (silf_blob)) + { + hb_blob_destroy (silf_blob); + return nullptr; + } + hb_blob_destroy (silf_blob); + + hb_graphite2_face_data_t *data = (hb_graphite2_face_data_t *) hb_calloc (1, sizeof (hb_graphite2_face_data_t)); + if (unlikely (!data)) + return nullptr; + + data->face = face; + const gr_face_ops ops = {sizeof(gr_face_ops), &hb_graphite2_get_table, NULL}; + data->grface = gr_make_face_with_ops (data, &ops, gr_face_preloadAll); + + if (unlikely (!data->grface)) { + hb_free (data); + return nullptr; + } + + return data; +} + +void +_hb_graphite2_shaper_face_data_destroy (hb_graphite2_face_data_t *data) +{ + hb_graphite2_tablelist_t *tlist = data->tlist; + + while (tlist) + { + hb_graphite2_tablelist_t *old = tlist; + hb_blob_destroy (tlist->blob); + tlist = tlist->next; + hb_free (old); + } + + gr_face_destroy (data->grface); + + hb_free (data); +} + +/** + * hb_graphite2_face_get_gr_face: (skip) + * @face: @hb_face_t to query + * + * Fetches the Graphite2 gr_face corresponding to the specified + * #hb_face_t face object. + * + * Return value: the gr_face found + * + * Since: 0.9.10 + */ +gr_face * +hb_graphite2_face_get_gr_face (hb_face_t *face) +{ + const hb_graphite2_face_data_t *data = face->data.graphite2; + return data ? data->grface : nullptr; +} + + +/* + * shaper font data + */ + +struct hb_graphite2_font_data_t {}; + +hb_graphite2_font_data_t * +_hb_graphite2_shaper_font_data_create (hb_font_t *font HB_UNUSED) +{ + return (hb_graphite2_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; +} + +void +_hb_graphite2_shaper_font_data_destroy (hb_graphite2_font_data_t *data HB_UNUSED) +{ +} + +#ifndef HB_DISABLE_DEPRECATED +/** + * hb_graphite2_font_get_gr_font: (skip) + * @font: An #hb_font_t + * + * Always returns `NULL`. Use hb_graphite2_face_get_gr_face() instead. + * + * Return value: (nullable): Graphite2 font associated with @font. + * + * Since: 0.9.10 + * Deprecated: 1.4.2 + */ +gr_font * +hb_graphite2_font_get_gr_font (hb_font_t *font HB_UNUSED) +{ + return nullptr; +} +#endif + + +/* + * shaper + */ + +struct hb_graphite2_cluster_t { + unsigned int base_char; + unsigned int num_chars; + unsigned int base_glyph; + unsigned int num_glyphs; + unsigned int cluster; + int advance; +}; + +hb_bool_t +_hb_graphite2_shape (hb_shape_plan_t *shape_plan HB_UNUSED, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features) +{ + hb_face_t *face = font->face; + gr_face *grface = face->data.graphite2->grface; + + const char *lang = hb_language_to_string (hb_buffer_get_language (buffer)); + const char *lang_end = lang ? strchr (lang, '-') : nullptr; + int lang_len = lang_end ? lang_end - lang : -1; + gr_feature_val *feats = gr_face_featureval_for_lang (grface, lang ? hb_tag_from_string (lang, lang_len) : 0); + + for (unsigned int i = 0; i < num_features; i++) + { + const gr_feature_ref *fref = gr_face_find_fref (grface, features[i].tag); + if (fref) + gr_fref_set_feature_value (fref, features[i].value, feats); + } + + hb_direction_t direction = buffer->props.direction; + hb_direction_t horiz_dir = hb_script_get_horizontal_direction (buffer->props.script); + /* TODO vertical: + * The only BTT vertical script is Ogham, but it's not clear to me whether OpenType + * Ogham fonts are supposed to be implemented BTT or not. Need to research that + * first. */ + if ((HB_DIRECTION_IS_HORIZONTAL (direction) && + direction != horiz_dir && horiz_dir != HB_DIRECTION_INVALID) || + (HB_DIRECTION_IS_VERTICAL (direction) && + direction != HB_DIRECTION_TTB)) + { + hb_buffer_reverse_clusters (buffer); + direction = HB_DIRECTION_REVERSE (direction); + } + + gr_segment *seg = nullptr; + const gr_slot *is; + unsigned int ci = 0, ic = 0; + unsigned int curradvx = 0, curradvy = 0; + + unsigned int scratch_size; + hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size); + + uint32_t *chars = (uint32_t *) scratch; + + for (unsigned int i = 0; i < buffer->len; ++i) + chars[i] = buffer->info[i].codepoint; + + seg = gr_make_seg (nullptr, grface, + HB_TAG_NONE, // https://github.com/harfbuzz/harfbuzz/issues/3439#issuecomment-1442650148 + feats, + gr_utf32, chars, buffer->len, + 2 | (direction == HB_DIRECTION_RTL ? 1 : 0)); + + if (unlikely (!seg)) { + if (feats) gr_featureval_destroy (feats); + return false; + } + + unsigned int glyph_count = gr_seg_n_slots (seg); + if (unlikely (!glyph_count)) { + if (feats) gr_featureval_destroy (feats); + gr_seg_destroy (seg); + buffer->len = 0; + return true; + } + + (void) buffer->ensure (glyph_count); + scratch = buffer->get_scratch_buffer (&scratch_size); + while ((DIV_CEIL (sizeof (hb_graphite2_cluster_t) * buffer->len, sizeof (*scratch)) + + DIV_CEIL (sizeof (hb_codepoint_t) * glyph_count, sizeof (*scratch))) > scratch_size) + { + if (unlikely (!buffer->ensure (buffer->allocated * 2))) + { + if (feats) gr_featureval_destroy (feats); + gr_seg_destroy (seg); + return false; + } + scratch = buffer->get_scratch_buffer (&scratch_size); + } + +#define ALLOCATE_ARRAY(Type, name, len) \ + Type *name = (Type *) scratch; \ + do { \ + unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \ + assert (_consumed <= scratch_size); \ + scratch += _consumed; \ + scratch_size -= _consumed; \ + } while (0) + + ALLOCATE_ARRAY (hb_graphite2_cluster_t, clusters, buffer->len); + ALLOCATE_ARRAY (hb_codepoint_t, gids, glyph_count); + +#undef ALLOCATE_ARRAY + + hb_memset (clusters, 0, sizeof (clusters[0]) * buffer->len); + + hb_codepoint_t *pg = gids; + clusters[0].cluster = buffer->info[0].cluster; + unsigned int upem = hb_face_get_upem (face); + float xscale = (float) font->x_scale / upem; + float yscale = (float) font->y_scale / upem; + yscale *= yscale / xscale; + unsigned int curradv = 0; + if (HB_DIRECTION_IS_BACKWARD (direction)) + { + curradv = gr_slot_origin_X(gr_seg_first_slot(seg)) * xscale; + clusters[0].advance = gr_seg_advance_X(seg) * xscale - curradv; + } + else + clusters[0].advance = 0; + for (is = gr_seg_first_slot (seg), ic = 0; is; is = gr_slot_next_in_segment (is), ic++) + { + unsigned int before = gr_slot_before (is); + unsigned int after = gr_slot_after (is); + *pg = gr_slot_gid (is); + pg++; + while (clusters[ci].base_char > before && ci) + { + clusters[ci-1].num_chars += clusters[ci].num_chars; + clusters[ci-1].num_glyphs += clusters[ci].num_glyphs; + clusters[ci-1].advance += clusters[ci].advance; + ci--; + } + + if (gr_slot_can_insert_before (is) && clusters[ci].num_chars && before >= clusters[ci].base_char + clusters[ci].num_chars) + { + hb_graphite2_cluster_t *c = clusters + ci + 1; + c->base_char = clusters[ci].base_char + clusters[ci].num_chars; + c->cluster = buffer->info[c->base_char].cluster; + c->num_chars = before - c->base_char; + c->base_glyph = ic; + c->num_glyphs = 0; + if (HB_DIRECTION_IS_BACKWARD (direction)) + { + c->advance = curradv - gr_slot_origin_X(is) * xscale; + curradv -= c->advance; + } + else + { + auto origin_X = gr_slot_origin_X (is) * xscale; + c->advance = 0; + clusters[ci].advance += origin_X - curradv; + curradv = origin_X; + } + ci++; + } + clusters[ci].num_glyphs++; + + if (clusters[ci].base_char + clusters[ci].num_chars < after + 1) + clusters[ci].num_chars = after + 1 - clusters[ci].base_char; + } + + if (HB_DIRECTION_IS_BACKWARD (direction)) + clusters[ci].advance += curradv; + else + clusters[ci].advance += gr_seg_advance_X(seg) * xscale - curradv; + ci++; + + for (unsigned int i = 0; i < ci; ++i) + { + for (unsigned int j = 0; j < clusters[i].num_glyphs; ++j) + { + hb_glyph_info_t *info = &buffer->info[clusters[i].base_glyph + j]; + info->codepoint = gids[clusters[i].base_glyph + j]; + info->cluster = clusters[i].cluster; + info->var1.i32 = clusters[i].advance; // all glyphs in the cluster get the same advance + } + } + buffer->len = glyph_count; + + /* Positioning. */ + unsigned int currclus = UINT_MAX; + const hb_glyph_info_t *info = buffer->info; + hb_glyph_position_t *pPos = hb_buffer_get_glyph_positions (buffer, nullptr); + if (!HB_DIRECTION_IS_BACKWARD (direction)) + { + curradvx = 0; + for (is = gr_seg_first_slot (seg); is; pPos++, ++info, is = gr_slot_next_in_segment (is)) + { + pPos->x_offset = gr_slot_origin_X (is) * xscale - curradvx; + pPos->y_offset = gr_slot_origin_Y (is) * yscale - curradvy; + if (info->cluster != currclus) { + pPos->x_advance = info->var1.i32; + curradvx += pPos->x_advance; + currclus = info->cluster; + } else + pPos->x_advance = 0.; + + pPos->y_advance = gr_slot_advance_Y (is, grface, nullptr) * yscale; + curradvy += pPos->y_advance; + } + } + else + { + curradvx = gr_seg_advance_X(seg) * xscale; + for (is = gr_seg_first_slot (seg); is; pPos++, info++, is = gr_slot_next_in_segment (is)) + { + if (info->cluster != currclus) + { + pPos->x_advance = info->var1.i32; + curradvx -= pPos->x_advance; + currclus = info->cluster; + } else + pPos->x_advance = 0.; + + pPos->y_advance = gr_slot_advance_Y (is, grface, nullptr) * yscale; + curradvy -= pPos->y_advance; + pPos->x_offset = gr_slot_origin_X (is) * xscale - info->var1.i32 - curradvx + pPos->x_advance; + pPos->y_offset = gr_slot_origin_Y (is) * yscale - curradvy; + } + hb_buffer_reverse_clusters (buffer); + } + + if (feats) gr_featureval_destroy (feats); + gr_seg_destroy (seg); + + buffer->clear_glyph_flags (); + buffer->unsafe_to_break (); + + return true; +} + + +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-graphite2.h b/src/3rdparty/harfbuzz-ng/src/hb-graphite2.h new file mode 100644 index 00000000000..ee9229b8b00 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-graphite2.h @@ -0,0 +1,61 @@ +/* + * Copyright © 2011 Martin Hosken + * Copyright © 2011 SIL International + * + * 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. + */ + +#ifndef HB_GRAPHITE2_H +#define HB_GRAPHITE2_H + +#include "hb.h" + +#include + +HB_BEGIN_DECLS + +/** + * HB_GRAPHITE2_TAG_SILF: + * + * The #hb_tag_t tag for the `Silf` table, which holds Graphite + * features. + * + * For more information, see http://graphite.sil.org/ + * + **/ +#define HB_GRAPHITE2_TAG_SILF HB_TAG('S','i','l','f') + + +HB_EXTERN gr_face * +hb_graphite2_face_get_gr_face (hb_face_t *face); + +#ifndef HB_DISABLE_DEPRECATED + +HB_DEPRECATED_FOR (hb_graphite2_face_get_gr_face) +HB_EXTERN gr_font * +hb_graphite2_font_get_gr_font (hb_font_t *font); + +#endif + + +HB_END_DECLS + +#endif /* HB_GRAPHITE2_H */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-icu.cc b/src/3rdparty/harfbuzz-ng/src/hb-icu.cc new file mode 100644 index 00000000000..e46401f7a61 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-icu.cc @@ -0,0 +1,290 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2009 Keith Stribley + * Copyright © 2011 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#ifdef HAVE_ICU + +#include "hb-icu.h" + +#include "hb-machinery.hh" + +#include +#include +#include +#include +#include + +/* ICU extra semicolon, fixed since 65, https://github.com/unicode-org/icu/commit/480bec3 */ +#if U_ICU_VERSION_MAJOR_NUM < 65 && (defined(__GNUC__) || defined(__clang__)) +#define HB_ICU_EXTRA_SEMI_IGNORED +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wextra-semi-stmt" +#endif + +/** + * SECTION:hb-icu + * @title: hb-icu + * @short_description: ICU integration + * @include: hb-icu.h + * + * Functions for using HarfBuzz with the International Components for Unicode + * (ICU) library. HarfBuzz supports using ICU to provide Unicode data, by attaching + * ICU functions to the virtual methods in a #hb_unicode_funcs_t function + * structure. + **/ + +/** + * hb_icu_script_to_script: + * @script: The UScriptCode identifier to query + * + * Fetches the #hb_script_t script that corresponds to the + * specified UScriptCode identifier. + * + * Return value: the #hb_script_t script found + * + **/ + +hb_script_t +hb_icu_script_to_script (UScriptCode script) +{ + if (unlikely (script == USCRIPT_INVALID_CODE)) + return HB_SCRIPT_INVALID; + + return hb_script_from_string (uscript_getShortName (script), -1); +} + +/** + * hb_icu_script_from_script: + * @script: The #hb_script_t script to query + * + * Fetches the UScriptCode identifier that corresponds to the + * specified #hb_script_t script. + * + * Return value: the UScriptCode identifier found + * + **/ +UScriptCode +hb_icu_script_from_script (hb_script_t script) +{ + if (unlikely (script == HB_SCRIPT_INVALID)) + return USCRIPT_INVALID_CODE; + + unsigned int numScriptCode = 1 + u_getIntPropertyMaxValue (UCHAR_SCRIPT); + for (unsigned int i = 0; i < numScriptCode; i++) + if (unlikely (hb_icu_script_to_script ((UScriptCode) i) == script)) + return (UScriptCode) i; + + return USCRIPT_UNKNOWN; +} + + +static hb_unicode_combining_class_t +hb_icu_unicode_combining_class (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) + +{ + return (hb_unicode_combining_class_t) u_getCombiningClass (unicode); +} + +static hb_unicode_general_category_t +hb_icu_unicode_general_category (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + switch (u_getIntPropertyValue(unicode, UCHAR_GENERAL_CATEGORY)) + { + case U_UNASSIGNED: return HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED; + + case U_UPPERCASE_LETTER: return HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER; + case U_LOWERCASE_LETTER: return HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER; + case U_TITLECASE_LETTER: return HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER; + case U_MODIFIER_LETTER: return HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER; + case U_OTHER_LETTER: return HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER; + + case U_NON_SPACING_MARK: return HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK; + case U_ENCLOSING_MARK: return HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK; + case U_COMBINING_SPACING_MARK: return HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK; + + case U_DECIMAL_DIGIT_NUMBER: return HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER; + case U_LETTER_NUMBER: return HB_UNICODE_GENERAL_CATEGORY_LETTER_NUMBER; + case U_OTHER_NUMBER: return HB_UNICODE_GENERAL_CATEGORY_OTHER_NUMBER; + + case U_SPACE_SEPARATOR: return HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR; + case U_LINE_SEPARATOR: return HB_UNICODE_GENERAL_CATEGORY_LINE_SEPARATOR; + case U_PARAGRAPH_SEPARATOR: return HB_UNICODE_GENERAL_CATEGORY_PARAGRAPH_SEPARATOR; + + case U_CONTROL_CHAR: return HB_UNICODE_GENERAL_CATEGORY_CONTROL; + case U_FORMAT_CHAR: return HB_UNICODE_GENERAL_CATEGORY_FORMAT; + case U_PRIVATE_USE_CHAR: return HB_UNICODE_GENERAL_CATEGORY_PRIVATE_USE; + case U_SURROGATE: return HB_UNICODE_GENERAL_CATEGORY_SURROGATE; + + + case U_DASH_PUNCTUATION: return HB_UNICODE_GENERAL_CATEGORY_DASH_PUNCTUATION; + case U_START_PUNCTUATION: return HB_UNICODE_GENERAL_CATEGORY_OPEN_PUNCTUATION; + case U_END_PUNCTUATION: return HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION; + case U_CONNECTOR_PUNCTUATION: return HB_UNICODE_GENERAL_CATEGORY_CONNECT_PUNCTUATION; + case U_OTHER_PUNCTUATION: return HB_UNICODE_GENERAL_CATEGORY_OTHER_PUNCTUATION; + + case U_MATH_SYMBOL: return HB_UNICODE_GENERAL_CATEGORY_MATH_SYMBOL; + case U_CURRENCY_SYMBOL: return HB_UNICODE_GENERAL_CATEGORY_CURRENCY_SYMBOL; + case U_MODIFIER_SYMBOL: return HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL; + case U_OTHER_SYMBOL: return HB_UNICODE_GENERAL_CATEGORY_OTHER_SYMBOL; + + case U_INITIAL_PUNCTUATION: return HB_UNICODE_GENERAL_CATEGORY_INITIAL_PUNCTUATION; + case U_FINAL_PUNCTUATION: return HB_UNICODE_GENERAL_CATEGORY_FINAL_PUNCTUATION; + } + + return HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED; +} + +static hb_codepoint_t +hb_icu_unicode_mirroring (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + return u_charMirror(unicode); +} + +static hb_script_t +hb_icu_unicode_script (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + UErrorCode status = U_ZERO_ERROR; + UScriptCode scriptCode = uscript_getScript(unicode, &status); + + if (unlikely (U_FAILURE (status))) + return HB_SCRIPT_UNKNOWN; + + return hb_icu_script_to_script (scriptCode); +} + +static hb_bool_t +hb_icu_unicode_compose (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab, + void *user_data) +{ + const UNormalizer2 *normalizer = (const UNormalizer2 *) user_data; + UChar32 ret = unorm2_composePair (normalizer, a, b); + if (ret < 0) return false; + *ab = ret; + return true; +} + +static hb_bool_t +hb_icu_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t ab, + hb_codepoint_t *a, + hb_codepoint_t *b, + void *user_data) +{ + const UNormalizer2 *normalizer = (const UNormalizer2 *) user_data; + UChar decomposed[4]; + int len; + UErrorCode icu_err = U_ZERO_ERROR; + len = unorm2_getRawDecomposition (normalizer, ab, decomposed, + ARRAY_LENGTH (decomposed), &icu_err); + if (U_FAILURE (icu_err) || len < 0) return false; + + len = u_countChar32 (decomposed, len); + if (len == 1) + { + U16_GET_UNSAFE (decomposed, 0, *a); + *b = 0; + return *a != ab; + } + else if (len == 2) + { + len = 0; + U16_NEXT_UNSAFE (decomposed, len, *a); + U16_NEXT_UNSAFE (decomposed, len, *b); + } + return true; +} + + +static inline void free_static_icu_funcs (); + +static struct hb_icu_unicode_funcs_lazy_loader_t : hb_unicode_funcs_lazy_loader_t +{ + static hb_unicode_funcs_t *create () + { + void *user_data = nullptr; + UErrorCode icu_err = U_ZERO_ERROR; + user_data = (void *) unorm2_getNFCInstance (&icu_err); + assert (user_data); + + hb_unicode_funcs_t *funcs = hb_unicode_funcs_create (nullptr); + + hb_unicode_funcs_set_combining_class_func (funcs, hb_icu_unicode_combining_class, nullptr, nullptr); + hb_unicode_funcs_set_general_category_func (funcs, hb_icu_unicode_general_category, nullptr, nullptr); + hb_unicode_funcs_set_mirroring_func (funcs, hb_icu_unicode_mirroring, nullptr, nullptr); + hb_unicode_funcs_set_script_func (funcs, hb_icu_unicode_script, nullptr, nullptr); + hb_unicode_funcs_set_compose_func (funcs, hb_icu_unicode_compose, user_data, nullptr); + hb_unicode_funcs_set_decompose_func (funcs, hb_icu_unicode_decompose, user_data, nullptr); + + hb_unicode_funcs_make_immutable (funcs); + + hb_atexit (free_static_icu_funcs); + + return funcs; + } +} static_icu_funcs; + +static inline +void free_static_icu_funcs () +{ + static_icu_funcs.free_instance (); +} + +/** + * hb_icu_get_unicode_funcs: + * + * Fetches a Unicode-functions structure that is populated + * with the appropriate ICU function for each method. + * + * Return value: (transfer none): a pointer to the #hb_unicode_funcs_t Unicode-functions structure + * + * Since: 0.9.38 + **/ +hb_unicode_funcs_t * +hb_icu_get_unicode_funcs () +{ + return static_icu_funcs.get_unconst (); +} + +#ifdef HB_ICU_EXTRA_SEMI_IGNORED +#pragma GCC diagnostic pop +#endif + +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-icu.h b/src/3rdparty/harfbuzz-ng/src/hb-icu.h new file mode 100644 index 00000000000..2db6a7b6797 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-icu.h @@ -0,0 +1,52 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2011 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_ICU_H +#define HB_ICU_H + +#include "hb.h" + +#include + +HB_BEGIN_DECLS + + +HB_EXTERN hb_script_t +hb_icu_script_to_script (UScriptCode script); + +HB_EXTERN UScriptCode +hb_icu_script_from_script (hb_script_t script); + + +HB_EXTERN hb_unicode_funcs_t * +hb_icu_get_unicode_funcs (void); + + +HB_END_DECLS + +#endif /* HB_ICU_H */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-iter.hh b/src/3rdparty/harfbuzz-ng/src/hb-iter.hh new file mode 100644 index 00000000000..61e05180be4 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-iter.hh @@ -0,0 +1,1027 @@ +/* + * Copyright © 2018 Google, Inc. + * Copyright © 2019 Facebook, 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): Behdad Esfahbod + * Facebook Author(s): Behdad Esfahbod + */ + +#ifndef HB_ITER_HH +#define HB_ITER_HH + +#include "hb.hh" +#include "hb-algs.hh" +#include "hb-meta.hh" + + +/* Unified iterator object. + * + * The goal of this template is to make the same iterator interface + * available to all types, and make it very easy and compact to use. + * hb_iter_tator objects are small, light-weight, objects that can be + * copied by value. If the collection / object being iterated on + * is writable, then the iterator returns lvalues, otherwise it + * returns rvalues. + * + * If iterator implementation implements operator!=, then it can be + * used in range-based for loop. That already happens if the iterator + * is random-access. Otherwise, the range-based for loop incurs + * one traversal to find end(), which can be avoided if written + * as a while-style for loop, or if iterator implements a faster + * __end__() method. */ + +/* + * Base classes for iterators. + */ + +/* Base class for all iterators. */ +template +struct hb_iter_t +{ + typedef Item item_t; + constexpr unsigned get_item_size () const { return hb_static_size (Item); } + static constexpr bool is_iterator = true; + static constexpr bool is_random_access_iterator = false; + static constexpr bool is_sorted_iterator = false; + static constexpr bool has_fast_len = false; // Should be checked in combination with is_random_access_iterator. + + private: + /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */ + const iter_t* thiz () const { return static_cast (this); } + iter_t* thiz () { return static_cast< iter_t *> (this); } + public: + + /* Operators. */ + iter_t iter () const { return *thiz(); } + iter_t operator + () const { return *thiz(); } + iter_t _begin () const { return *thiz(); } + iter_t begin () const { return _begin (); } + iter_t _end () const { return thiz()->__end__ (); } + iter_t end () const { return _end (); } + explicit operator bool () const { return thiz()->__more__ (); } + unsigned len () const { return thiz()->__len__ (); } + /* The following can only be enabled if item_t is reference type. Otherwise + * it will be returning pointer to temporary rvalue. */ + template ::value)> + hb_remove_reference* operator -> () const { return std::addressof (**thiz()); } + item_t operator * () const { return thiz()->__item__ (); } + item_t operator * () { return thiz()->__item__ (); } + item_t operator [] (unsigned i) const { return thiz()->__item_at__ (i); } + item_t operator [] (unsigned i) { return thiz()->__item_at__ (i); } + iter_t& operator += (unsigned count) & { thiz()->__forward__ (count); return *thiz(); } + iter_t operator += (unsigned count) && { thiz()->__forward__ (count); return *thiz(); } + iter_t& operator ++ () & { thiz()->__next__ (); return *thiz(); } + iter_t operator ++ () && { thiz()->__next__ (); return *thiz(); } + iter_t& operator -= (unsigned count) & { thiz()->__rewind__ (count); return *thiz(); } + iter_t operator -= (unsigned count) && { thiz()->__rewind__ (count); return *thiz(); } + iter_t& operator -- () & { thiz()->__prev__ (); return *thiz(); } + iter_t operator -- () && { thiz()->__prev__ (); return *thiz(); } + iter_t operator + (unsigned count) const { auto c = thiz()->iter (); c += count; return c; } + friend iter_t operator + (unsigned count, const iter_t &it) { return it + count; } + iter_t operator ++ (int) { iter_t c (*thiz()); ++*thiz(); return c; } + iter_t operator - (unsigned count) const { auto c = thiz()->iter (); c -= count; return c; } + iter_t operator -- (int) { iter_t c (*thiz()); --*thiz(); return c; } + template + iter_t& operator >> (T &v) & { v = **thiz(); ++*thiz(); return *thiz(); } + template + iter_t operator >> (T &v) && { v = **thiz(); ++*thiz(); return *thiz(); } + template + iter_t& operator << (const T v) & { **thiz() = v; ++*thiz(); return *thiz(); } + template + iter_t operator << (const T v) && { **thiz() = v; ++*thiz(); return *thiz(); } + + protected: + hb_iter_t () = default; + hb_iter_t (const hb_iter_t &o HB_UNUSED) = default; + hb_iter_t (hb_iter_t &&o HB_UNUSED) = default; + hb_iter_t& operator = (const hb_iter_t &o HB_UNUSED) = default; + hb_iter_t& operator = (hb_iter_t &&o HB_UNUSED) = default; +}; + +#define HB_ITER_USING(Name) \ + using item_t = typename Name::item_t; \ + using Name::_begin; \ + using Name::begin; \ + using Name::_end; \ + using Name::end; \ + using Name::get_item_size; \ + using Name::is_iterator; \ + using Name::iter; \ + using Name::operator bool; \ + using Name::len; \ + using Name::operator ->; \ + using Name::operator *; \ + using Name::operator []; \ + using Name::operator +=; \ + using Name::operator ++; \ + using Name::operator -=; \ + using Name::operator --; \ + using Name::operator +; \ + using Name::operator -; \ + using Name::operator >>; \ + using Name::operator <<; \ + static_assert (true, "") + +/* Returns iterator / item type of a type. */ +template +using hb_iter_type = decltype (hb_deref (hb_declval (Iterable)).iter ()); +template +using hb_item_type = decltype (*hb_deref (hb_declval (Iterable)).iter ()); + + +template struct hb_array_t; +template struct hb_sorted_array_t; + +struct +{ + template hb_iter_type + operator () (T&& c) const + { return hb_deref (std::forward (c)).iter (); } + + /* Specialization for C arrays. */ + + template inline hb_array_t + operator () (Type *array, unsigned int length) const + { return hb_array_t (array, length); } + + template hb_array_t + operator () (Type (&array)[length]) const + { return hb_array_t (array, length); } + +} +HB_FUNCOBJ (hb_iter); +struct +{ + template auto + impl (T&& c, hb_priority<1>) const HB_RETURN (unsigned, c.len ()) + + template auto + impl (T&& c, hb_priority<0>) const HB_RETURN (unsigned, c.len) + + public: + + template auto + operator () (T&& c) const HB_RETURN (unsigned, impl (std::forward (c), hb_prioritize)) +} +HB_FUNCOBJ (hb_len); + +/* Mixin to fill in what the subclass doesn't provide. */ +template +struct hb_iter_fallback_mixin_t +{ + private: + /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */ + const iter_t* thiz () const { return static_cast (this); } + iter_t* thiz () { return static_cast< iter_t *> (this); } + public: + + /* Access: Implement __item__(), or __item_at__() if random-access. */ + item_t __item__ () const { return (*thiz())[0]; } + item_t __item_at__ (unsigned i) const { return *(*thiz() + i); } + + /* Termination: Implement __more__(), or __len__() if random-access. */ + bool __more__ () const { return bool (thiz()->len ()); } + unsigned __len__ () const + { iter_t c (*thiz()); unsigned l = 0; while (c) { c++; l++; } return l; } + + /* Advancing: Implement __next__(), or __forward__() if random-access. */ + void __next__ () { *thiz() += 1; } + void __forward__ (unsigned n) { while (*thiz() && n--) ++*thiz(); } + + /* Rewinding: Implement __prev__() or __rewind__() if bidirectional. */ + void __prev__ () { *thiz() -= 1; } + void __rewind__ (unsigned n) { while (*thiz() && n--) --*thiz(); } + + /* Range-based for: Implement __end__() if can be done faster, + * and operator!=. */ + iter_t __end__ () const + { + if (thiz()->is_random_access_iterator) + return *thiz() + thiz()->len (); + /* Above expression loops twice. Following loops once. */ + auto it = *thiz(); + while (it) ++it; + return it; + } + + protected: + hb_iter_fallback_mixin_t () = default; + hb_iter_fallback_mixin_t (const hb_iter_fallback_mixin_t &o HB_UNUSED) = default; + hb_iter_fallback_mixin_t (hb_iter_fallback_mixin_t &&o HB_UNUSED) = default; + hb_iter_fallback_mixin_t& operator = (const hb_iter_fallback_mixin_t &o HB_UNUSED) = default; + hb_iter_fallback_mixin_t& operator = (hb_iter_fallback_mixin_t &&o HB_UNUSED) = default; +}; + +template +struct hb_iter_with_fallback_t : + hb_iter_t, + hb_iter_fallback_mixin_t +{ + protected: + hb_iter_with_fallback_t () = default; + hb_iter_with_fallback_t (const hb_iter_with_fallback_t &o HB_UNUSED) = default; + hb_iter_with_fallback_t (hb_iter_with_fallback_t &&o HB_UNUSED) = default; + hb_iter_with_fallback_t& operator = (const hb_iter_with_fallback_t &o HB_UNUSED) = default; + hb_iter_with_fallback_t& operator = (hb_iter_with_fallback_t &&o HB_UNUSED) = default; +}; + +/* + * Meta-programming predicates. + */ + +/* hb_is_iterator() / hb_is_iterator_of() */ + +template +struct hb_is_iterator_of +{ + template + static hb_true_type impl (hb_priority<2>, hb_iter_t> *); + static hb_false_type impl (hb_priority<0>, const void *); + + public: + static constexpr bool value = decltype (impl (hb_prioritize, hb_declval (Iter*)))::value; +}; +#define hb_is_iterator_of(Iter, Item) hb_is_iterator_of::value +#define hb_is_iterator(Iter) hb_is_iterator_of (Iter, typename Iter::item_t) +#define hb_is_sorted_iterator_of(Iter, Item) (hb_is_iterator_of::value && Iter::is_sorted_iterator) +#define hb_is_sorted_iterator(Iter) hb_is_sorted_iterator_of (Iter, typename Iter::item_t) + +/* hb_is_iterable() */ + +template +struct hb_is_iterable +{ + private: + + template + static auto impl (hb_priority<1>) -> decltype (hb_declval (U).iter (), hb_true_type ()); + + template + static hb_false_type impl (hb_priority<0>); + + public: + static constexpr bool value = decltype (impl (hb_prioritize))::value; +}; +#define hb_is_iterable(Iterable) hb_is_iterable::value + +/* hb_is_source_of() / hb_is_sink_of() */ + +template +struct hb_is_source_of +{ + private: + template ))> + static hb_true_type impl (hb_priority<2>); + template + static auto impl (hb_priority<1>) -> decltype (hb_declval (Iter2) >> hb_declval (Item &), hb_true_type ()); + static hb_false_type impl (hb_priority<0>); + + public: + static constexpr bool value = decltype (impl (hb_prioritize))::value; +}; +#define hb_is_source_of(Iter, Item) hb_is_source_of::value + +template +struct hb_is_sink_of +{ + private: + template ))> + static hb_true_type impl (hb_priority<2>); + template + static auto impl (hb_priority<1>) -> decltype (hb_declval (Iter2) << hb_declval (Item), hb_true_type ()); + static hb_false_type impl (hb_priority<0>); + + public: + static constexpr bool value = decltype (impl (hb_prioritize))::value; +}; +#define hb_is_sink_of(Iter, Item) hb_is_sink_of::value + +/* This is commonly used, so define: */ +#define hb_is_sorted_source_of(Iter, Item) \ + (hb_is_source_of(Iter, Item) && Iter::is_sorted_iterator) + + +/* Range-based 'for' for iterables. */ + +template +static inline auto begin (Iterable&& iterable) HB_AUTO_RETURN (hb_iter (iterable).begin ()) + +template +static inline auto end (Iterable&& iterable) HB_AUTO_RETURN (hb_iter (iterable).end ()) + +/* begin()/end() are NOT looked up non-ADL. So each namespace must declare them. + * Do it for namespace OT. */ +namespace OT { + +template +static inline auto begin (Iterable&& iterable) HB_AUTO_RETURN (hb_iter (iterable).begin ()) + +template +static inline auto end (Iterable&& iterable) HB_AUTO_RETURN (hb_iter (iterable).end ()) + +} + + +/* + * Adaptors, combiners, etc. + */ + +template +static inline auto +operator | (Lhs&& lhs, Rhs&& rhs) HB_AUTO_RETURN (std::forward (rhs) (std::forward (lhs))) + +/* hb_map(), hb_filter(), hb_reduce() */ + +enum class hb_function_sortedness_t { + NOT_SORTED, + RETAINS_SORTING, + SORTED, +}; + +template +struct hb_map_iter_t : + hb_iter_t, + decltype (hb_get (hb_declval (Proj), *hb_declval (Iter)))> +{ + hb_map_iter_t (const Iter& it, Proj f_) : it (it), f (f_) {} + + typedef decltype (hb_get (hb_declval (Proj), *hb_declval (Iter))) __item_t__; + static constexpr bool is_random_access_iterator = Iter::is_random_access_iterator; + static constexpr bool is_sorted_iterator = + Sorted == hb_function_sortedness_t::SORTED ? true : + Sorted == hb_function_sortedness_t::RETAINS_SORTING ? Iter::is_sorted_iterator : + false; + __item_t__ __item__ () const { return hb_get (f.get (), *it); } + __item_t__ __item_at__ (unsigned i) const { return hb_get (f.get (), it[i]); } + bool __more__ () const { return bool (it); } + unsigned __len__ () const { return it.len (); } + void __next__ () { ++it; } + void __forward__ (unsigned n) { it += n; } + void __prev__ () { --it; } + void __rewind__ (unsigned n) { it -= n; } + hb_map_iter_t __end__ () const { return hb_map_iter_t (it._end (), f); } + bool operator != (const hb_map_iter_t& o) const + { return it != o.it; } + + private: + Iter it; + mutable hb_reference_wrapper f; +}; + +template +struct hb_map_iter_factory_t +{ + hb_map_iter_factory_t (Proj f) : f (f) {} + + template + hb_map_iter_t + operator () (Iter it) + { return hb_map_iter_t (it, f); } + + private: + Proj f; +}; +struct +{ + template + hb_map_iter_factory_t + operator () (Proj&& f) const + { return hb_map_iter_factory_t (f); } +} +HB_FUNCOBJ (hb_map); +struct +{ + template + hb_map_iter_factory_t + operator () (Proj&& f) const + { return hb_map_iter_factory_t (f); } +} +HB_FUNCOBJ (hb_map_retains_sorting); +struct +{ + template + hb_map_iter_factory_t + operator () (Proj&& f) const + { return hb_map_iter_factory_t (f); } +} +HB_FUNCOBJ (hb_map_sorted); + +template +struct hb_filter_iter_t : + hb_iter_with_fallback_t, + typename Iter::item_t> +{ + hb_filter_iter_t (const Iter& it_, Pred p_, Proj f_) : it (it_), p (p_), f (f_) + { while (it && !hb_has (p.get (), hb_get (f.get (), *it))) ++it; } + + typedef typename Iter::item_t __item_t__; + static constexpr bool is_sorted_iterator = Iter::is_sorted_iterator; + __item_t__ __item__ () const { return *it; } + bool __more__ () const { return bool (it); } + void __next__ () { do ++it; while (it && !hb_has (p.get (), hb_get (f.get (), *it))); } + void __prev__ () { do --it; while (it && !hb_has (p.get (), hb_get (f.get (), *it))); } + hb_filter_iter_t __end__ () const { return hb_filter_iter_t (it._end (), p, f); } + bool operator != (const hb_filter_iter_t& o) const + { return it != o.it; } + + private: + Iter it; + mutable hb_reference_wrapper p; + mutable hb_reference_wrapper f; +}; +template +struct hb_filter_iter_factory_t +{ + hb_filter_iter_factory_t (Pred p, Proj f) : p (p), f (f) {} + + template + hb_filter_iter_t + operator () (Iter it) + { return hb_filter_iter_t (it, p, f); } + + private: + Pred p; + Proj f; +}; +struct +{ + template + hb_filter_iter_factory_t + operator () (Pred&& p = hb_identity, Proj&& f = hb_identity) const + { return hb_filter_iter_factory_t (p, f); } +} +HB_FUNCOBJ (hb_filter); + +template +struct hb_reduce_t +{ + hb_reduce_t (Redu r, InitT init_value) : r (r), init_value (init_value) {} + + template > + AccuT + operator () (Iter it) + { + AccuT value = init_value; + for (; it; ++it) + value = r (value, *it); + return value; + } + + private: + Redu r; + InitT init_value; +}; +struct +{ + template + hb_reduce_t + operator () (Redu&& r, InitT init_value) const + { return hb_reduce_t (r, init_value); } +} +HB_FUNCOBJ (hb_reduce); + + +/* hb_zip() */ + +template +struct hb_zip_iter_t : + hb_iter_t, + hb_pair_t> +{ + hb_zip_iter_t () {} + hb_zip_iter_t (const A& a, const B& b) : a (a), b (b) {} + + typedef hb_pair_t __item_t__; + static constexpr bool is_random_access_iterator = + A::is_random_access_iterator && + B::is_random_access_iterator; + /* Note. The following categorization is only valid if A is strictly sorted, + * ie. does NOT have duplicates. Previously I tried to categorize sortedness + * more granularly, see commits: + * + * 513762849a683914fc266a17ddf38f133cccf072 + * 4d3cf2adb669c345cc43832d11689271995e160a + * + * However, that was not enough, since hb_sorted_array_t, hb_sorted_vector_t, + * SortedArrayOf, etc all needed to be updated to add more variants. At that + * point I saw it not worth the effort, and instead we now deem all sorted + * collections as essentially strictly-sorted for the purposes of zip. + * + * The above assumption is not as bad as it sounds. Our "sorted" comes with + * no guarantees. It's just a contract, put in place to help you remember, + * and think about, whether an iterator you receive is expected to be + * sorted or not. As such, it's not perfect by definition, and should not + * be treated so. The inaccuracy here just errs in the direction of being + * more permissive, so your code compiles instead of erring on the side of + * marking your zipped iterator unsorted in which case your code won't + * compile. + * + * This semantical limitation does NOT affect logic in any other place I + * know of as of this writing. + */ + static constexpr bool is_sorted_iterator = A::is_sorted_iterator; + + __item_t__ __item__ () const { return __item_t__ (*a, *b); } + __item_t__ __item_at__ (unsigned i) const { return __item_t__ (a[i], b[i]); } + bool __more__ () const { return bool (a) && bool (b); } + unsigned __len__ () const { return hb_min (a.len (), b.len ()); } + void __next__ () { ++a; ++b; } + void __forward__ (unsigned n) { a += n; b += n; } + void __prev__ () { --a; --b; } + void __rewind__ (unsigned n) { a -= n; b -= n; } + hb_zip_iter_t __end__ () const { return hb_zip_iter_t (a._end (), b._end ()); } + /* Note, we should stop if ANY of the iters reaches end. As such two compare + * unequal if both items are unequal, NOT if either is unequal. */ + bool operator != (const hb_zip_iter_t& o) const + { return a != o.a && b != o.b; } + + private: + A a; + B b; +}; +struct +{ HB_PARTIALIZE(2); + template + hb_zip_iter_t, hb_iter_type> + operator () (A&& a, B&& b) const + { return hb_zip_iter_t, hb_iter_type> (hb_iter (a), hb_iter (b)); } +} +HB_FUNCOBJ (hb_zip); + +/* hb_concat() */ + +template +struct hb_concat_iter_t : + hb_iter_t, typename A::item_t> +{ + hb_concat_iter_t () {} + hb_concat_iter_t (A& a, B& b) : a (a), b (b) {} + hb_concat_iter_t (const A& a, const B& b) : a (a), b (b) {} + + + typedef typename A::item_t __item_t__; + static constexpr bool is_random_access_iterator = + A::is_random_access_iterator && + B::is_random_access_iterator; + static constexpr bool is_sorted_iterator = false; + + __item_t__ __item__ () const + { + if (!a) + return *b; + return *a; + } + + __item_t__ __item_at__ (unsigned i) const + { + unsigned a_len = a.len (); + if (i < a_len) + return a[i]; + return b[i - a_len]; + } + + bool __more__ () const { return bool (a) || bool (b); } + + unsigned __len__ () const { return a.len () + b.len (); } + + void __next__ () + { + if (a) + ++a; + else + ++b; + } + + void __forward__ (unsigned n) + { + if (!n) return; + if (!is_random_access_iterator) { + while (n-- && *this) { + (*this)++; + } + return; + } + + unsigned a_len = a.len (); + if (n > a_len) { + n -= a_len; + a.__forward__ (a_len); + b.__forward__ (n); + } else { + a.__forward__ (n); + } + } + + hb_concat_iter_t __end__ () const { return hb_concat_iter_t (a._end (), b._end ()); } + bool operator != (const hb_concat_iter_t& o) const + { + return a != o.a + || b != o.b; + } + + private: + A a; + B b; +}; +struct +{ HB_PARTIALIZE(2); + template + hb_concat_iter_t, hb_iter_type> + operator () (A&& a, B&& b) const + { return hb_concat_iter_t, hb_iter_type> (hb_iter (a), hb_iter (b)); } +} +HB_FUNCOBJ (hb_concat); + +/* hb_apply() */ + +template +struct hb_apply_t +{ + hb_apply_t (Appl a) : a (a) {} + + template + void operator () (Iter it) + { + for (; it; ++it) + (void) hb_invoke (a, *it); + } + + private: + Appl a; +}; +struct +{ + template hb_apply_t + operator () (Appl&& a) const + { return hb_apply_t (a); } + + template hb_apply_t + operator () (Appl *a) const + { return hb_apply_t (*a); } +} +HB_FUNCOBJ (hb_apply); + +/* hb_range()/hb_iota()/hb_repeat() */ + +template +struct hb_range_iter_t : + hb_iter_t, T> +{ + hb_range_iter_t (T start, T end_, S step) : v (start), end_ (end_for (start, end_, step)), step (step) {} + + typedef T __item_t__; + static constexpr bool is_random_access_iterator = true; + static constexpr bool is_sorted_iterator = true; + __item_t__ __item__ () const { return hb_ridentity (v); } + __item_t__ __item_at__ (unsigned j) const { return v + j * step; } + bool __more__ () const { return v != end_; } + unsigned __len__ () const { return !step ? UINT_MAX : (end_ - v) / step; } + void __next__ () { v += step; } + void __forward__ (unsigned n) { v += n * step; } + void __prev__ () { v -= step; } + void __rewind__ (unsigned n) { v -= n * step; } + hb_range_iter_t __end__ () const { return hb_range_iter_t (end_, end_, step); } + bool operator != (const hb_range_iter_t& o) const + { return v != o.v; } + + private: + static inline T end_for (T start, T end_, S step) + { + if (!step) + return end_; + auto res = (end_ - start) % step; + if (!res) + return end_; + end_ += step - res; + return end_; + } + + private: + T v; + T end_; + S step; +}; +struct +{ + template hb_range_iter_t + operator () (T end = (unsigned) -1) const + { return hb_range_iter_t (0, end, 1u); } + + template hb_range_iter_t + operator () (T start, T end, S step = 1u) const + { return hb_range_iter_t (start, end, step); } +} +HB_FUNCOBJ (hb_range); + +template +struct hb_iota_iter_t : + hb_iter_with_fallback_t, T> +{ + hb_iota_iter_t (T start, S step) : v (start), step (step) {} + + private: + + template + auto + inc (hb_type_identity s, hb_priority<1>) + -> hb_void_t (s), hb_declval ()))> + { v = hb_invoke (std::forward (s), v); } + + void + inc (S s, hb_priority<0>) + { v += s; } + + public: + + typedef T __item_t__; + static constexpr bool is_random_access_iterator = true; + static constexpr bool is_sorted_iterator = true; + __item_t__ __item__ () const { return hb_ridentity (v); } + bool __more__ () const { return true; } + unsigned __len__ () const { return UINT_MAX; } + void __next__ () { inc (step, hb_prioritize); } + void __prev__ () { v -= step; } + hb_iota_iter_t __end__ () const { return *this; } + bool operator != (const hb_iota_iter_t& o) const { return true; } + + private: + T v; + S step; +}; +struct +{ + template hb_iota_iter_t + operator () (T start = 0u, S step = 1u) const + { return hb_iota_iter_t (start, step); } +} +HB_FUNCOBJ (hb_iota); + +template +struct hb_repeat_iter_t : + hb_iter_t, T> +{ + hb_repeat_iter_t (T value) : v (value) {} + + typedef T __item_t__; + static constexpr bool is_random_access_iterator = true; + static constexpr bool is_sorted_iterator = true; + __item_t__ __item__ () const { return v; } + __item_t__ __item_at__ (unsigned j) const { return v; } + bool __more__ () const { return true; } + unsigned __len__ () const { return UINT_MAX; } + void __next__ () {} + void __forward__ (unsigned) {} + void __prev__ () {} + void __rewind__ (unsigned) {} + hb_repeat_iter_t __end__ () const { return *this; } + bool operator != (const hb_repeat_iter_t& o) const { return true; } + + private: + T v; +}; +struct +{ + template hb_repeat_iter_t + operator () (T value) const + { return hb_repeat_iter_t (value); } +} +HB_FUNCOBJ (hb_repeat); + +/* hb_enumerate()/hb_take() */ + +struct +{ + template + auto operator () (Iterable&& it, Index start = 0u) const HB_AUTO_RETURN + ( hb_zip (hb_iota (start), it) ) +} +HB_FUNCOBJ (hb_enumerate); + +struct +{ HB_PARTIALIZE(2); + template + auto operator () (Iterable&& it, unsigned count) const HB_AUTO_RETURN + ( hb_zip (hb_range (count), it) | hb_map_retains_sorting (hb_second) ) + + /* Specialization arrays. */ + + template inline hb_array_t + operator () (hb_array_t array, unsigned count) const + { return array.sub_array (0, count); } + + template inline hb_sorted_array_t + operator () (hb_sorted_array_t array, unsigned count) const + { return array.sub_array (0, count); } +} +HB_FUNCOBJ (hb_take); + +struct +{ HB_PARTIALIZE(2); + template + auto operator () (Iter it, unsigned count) const HB_AUTO_RETURN + ( + + hb_iota (it, hb_add (count)) + | hb_map (hb_take (count)) + | hb_take ((hb_len (it) + count - 1) / count) + ) +} +HB_FUNCOBJ (hb_chop); + +/* hb_sink() */ + +template +struct hb_sink_t +{ + hb_sink_t (Sink s) : s (s) {} + + template + void operator () (Iter it) + { + for (; it; ++it) + s << *it; + } + + private: + Sink s; +}; +struct +{ + template hb_sink_t + operator () (Sink&& s) const + { return hb_sink_t (s); } + + template hb_sink_t + operator () (Sink *s) const + { return hb_sink_t (*s); } +} +HB_FUNCOBJ (hb_sink); + +/* hb-drain: hb_sink to void / blackhole / /dev/null. */ + +struct +{ + template + void operator () (Iter it) const + { + for (; it; ++it) + (void) *it; + } +} +HB_FUNCOBJ (hb_drain); + +/* hb_unzip(): unzip and sink to two sinks. */ + +template +struct hb_unzip_t +{ + hb_unzip_t (Sink1 s1, Sink2 s2) : s1 (s1), s2 (s2) {} + + template + void operator () (Iter it) + { + for (; it; ++it) + { + const auto &v = *it; + s1 << v.first; + s2 << v.second; + } + } + + private: + Sink1 s1; + Sink2 s2; +}; +struct +{ + template hb_unzip_t + operator () (Sink1&& s1, Sink2&& s2) const + { return hb_unzip_t (s1, s2); } + + template hb_unzip_t + operator () (Sink1 *s1, Sink2 *s2) const + { return hb_unzip_t (*s1, *s2); } +} +HB_FUNCOBJ (hb_unzip); + + +/* hb-all, hb-any, hb-none. */ + +struct +{ + template + bool operator () (Iterable&& c, + Pred&& p = hb_identity, + Proj&& f = hb_identity) const + { + for (auto it = hb_iter (c); it; ++it) + if (!hb_match (std::forward (p), hb_get (std::forward (f), *it))) + return false; + return true; + } +} +HB_FUNCOBJ (hb_all); +struct +{ + template + bool operator () (Iterable&& c, + Pred&& p = hb_identity, + Proj&& f = hb_identity) const + { + for (auto it = hb_iter (c); it; ++it) + if (hb_match (std::forward (p), hb_get (std::forward (f), *it))) + return true; + return false; + } +} +HB_FUNCOBJ (hb_any); +struct +{ + template + bool operator () (Iterable&& c, + Pred&& p = hb_identity, + Proj&& f = hb_identity) const + { + for (auto it = hb_iter (c); it; ++it) + if (hb_match (std::forward (p), hb_get (std::forward (f), *it))) + return false; + return true; + } +} +HB_FUNCOBJ (hb_none); + +/* + * Algorithms operating on iterators. + */ + +template +inline void +hb_fill (C&& c, const V &v) +{ + for (auto i = hb_iter (c); i; i++) + *i = v; +} + +template +inline void +hb_copy (S&& is, D&& id) +{ + hb_iter (is) | hb_sink (id); +} + + +#endif /* HB_ITER_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-kern.hh b/src/3rdparty/harfbuzz-ng/src/hb-kern.hh new file mode 100644 index 00000000000..0462a0ea8ee --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-kern.hh @@ -0,0 +1,145 @@ +/* + * Copyright © 2017 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): Behdad Esfahbod + */ + +#ifndef HB_KERN_HH +#define HB_KERN_HH + +#include "hb-open-type.hh" +#include "hb-aat-layout-common.hh" +#include "hb-ot-layout-gpos-table.hh" + + +namespace OT { + + +template +struct hb_kern_machine_t +{ + hb_kern_machine_t (const Driver &driver_, + bool crossStream_ = false) : + driver (driver_), + crossStream (crossStream_) {} + + HB_NO_SANITIZE_SIGNED_INTEGER_OVERFLOW + void kern (hb_font_t *font, + hb_buffer_t *buffer, + hb_mask_t kern_mask, + bool scale = true) const + { + if (!buffer->message (font, "start kern")) + return; + + buffer->unsafe_to_concat (); + OT::hb_ot_apply_context_t c (1, font, buffer, hb_blob_get_empty ()); + c.set_lookup_mask (kern_mask); + c.set_lookup_props (OT::LookupFlag::IgnoreMarks); + auto &skippy_iter = c.iter_input; + + bool horizontal = HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction); + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + hb_glyph_position_t *pos = buffer->pos; + for (unsigned int idx = 0; idx < count;) + { + if (!(info[idx].mask & kern_mask)) + { + idx++; + continue; + } + + skippy_iter.reset (idx); + unsigned unsafe_to; + if (!skippy_iter.next (&unsafe_to)) + { + idx++; + continue; + } + + unsigned int i = idx; + unsigned int j = skippy_iter.idx; + + hb_position_t kern = driver.get_kerning (info[i].codepoint, + info[j].codepoint); + + + if (likely (!kern)) + goto skip; + + if (horizontal) + { + if (scale) + kern = font->em_scale_x (kern); + if (crossStream) + { + pos[j].y_offset = kern; + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; + } + else + { + hb_position_t kern1 = kern >> 1; + hb_position_t kern2 = kern - kern1; + pos[i].x_advance += kern1; + pos[j].x_advance += kern2; + pos[j].x_offset += kern2; + } + } + else + { + if (scale) + kern = font->em_scale_y (kern); + if (crossStream) + { + pos[j].x_offset = kern; + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; + } + else + { + hb_position_t kern1 = kern >> 1; + hb_position_t kern2 = kern - kern1; + pos[i].y_advance += kern1; + pos[j].y_advance += kern2; + pos[j].y_offset += kern2; + } + } + + buffer->unsafe_to_break (i, j + 1); + + skip: + idx = skippy_iter.idx; + } + + (void) buffer->message (font, "end kern"); + } + + const Driver &driver; + bool crossStream; +}; + + +} /* namespace OT */ + + +#endif /* HB_KERN_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-limits.hh b/src/3rdparty/harfbuzz-ng/src/hb-limits.hh new file mode 100644 index 00000000000..25c1e71e133 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-limits.hh @@ -0,0 +1,113 @@ +/* + * Copyright © 2022 Behdad Esfahbod + * + * 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. + */ + +#ifndef HB_LIMITS_HH +#define HB_LIMITS_HH + +#include "hb.hh" + + +#ifndef HB_BUFFER_MAX_LEN_FACTOR +#define HB_BUFFER_MAX_LEN_FACTOR 64 +#endif +#ifndef HB_BUFFER_MAX_LEN_MIN +#define HB_BUFFER_MAX_LEN_MIN 16384 +#endif +#ifndef HB_BUFFER_MAX_LEN_DEFAULT +#define HB_BUFFER_MAX_LEN_DEFAULT 0x3FFFFFFF /* Shaping more than a billion chars? Let us know! */ +#endif + +#ifndef HB_BUFFER_MAX_OPS_FACTOR +#define HB_BUFFER_MAX_OPS_FACTOR 1024 +#endif +#ifndef HB_BUFFER_MAX_OPS_MIN +#define HB_BUFFER_MAX_OPS_MIN 16384 +#endif +#ifndef HB_BUFFER_MAX_OPS_DEFAULT +#define HB_BUFFER_MAX_OPS_DEFAULT 0x1FFFFFFF /* Shaping more than a billion operations? Let us know! */ +#endif + + +#ifndef HB_MAX_NESTING_LEVEL +#define HB_MAX_NESTING_LEVEL 64 +#endif + + +#ifndef HB_MAX_CONTEXT_LENGTH +#define HB_MAX_CONTEXT_LENGTH 64 +#endif + +#ifndef HB_CLOSURE_MAX_STAGES +/* + * The maximum number of times a lookup can be applied during shaping. + * Used to limit the number of iterations of the closure algorithm. + * This must be larger than the number of times add_gsub_pause() is + * called in a collect_features call of any shaper. + */ +#define HB_CLOSURE_MAX_STAGES 12 +#endif + +#ifndef HB_MAX_SCRIPTS +#define HB_MAX_SCRIPTS 500 +#endif + +#ifndef HB_MAX_LANGSYS +#define HB_MAX_LANGSYS 2000 +#endif + +#ifndef HB_MAX_LANGSYS_FEATURE_COUNT +#define HB_MAX_LANGSYS_FEATURE_COUNT 50000 +#endif + +#ifndef HB_MAX_FEATURE_INDICES +#define HB_MAX_FEATURE_INDICES 1500 +#endif + +#ifndef HB_MAX_LOOKUP_VISIT_COUNT +#define HB_MAX_LOOKUP_VISIT_COUNT 35000 +#endif + + +#ifndef HB_GLYF_VAR_COMPOSITE_MAX_AXES +#define HB_GLYF_VAR_COMPOSITE_MAX_AXES 4096 +#endif + +#ifndef HB_GLYF_MAX_POINTS +#define HB_GLYF_MAX_POINTS 20000 +#endif + +#ifndef HB_GLYF_MAX_EDGE_COUNT +#define HB_GLYF_MAX_EDGE_COUNT 1024 +#endif + +#ifndef HB_CFF_MAX_OPS +#define HB_CFF_MAX_OPS 10000 +#endif + +#ifndef HB_COLRV1_MAX_EDGE_COUNT +#define HB_COLRV1_MAX_EDGE_COUNT 65536 +#endif + + +#endif /* HB_LIMITS_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-machinery.hh b/src/3rdparty/harfbuzz-ng/src/hb-machinery.hh new file mode 100644 index 00000000000..ecff94f1b64 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-machinery.hh @@ -0,0 +1,332 @@ +/* + * Copyright © 2007,2008,2009,2010 Red Hat, Inc. + * Copyright © 2012,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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_MACHINERY_HH +#define HB_MACHINERY_HH + +#include "hb.hh" +#include "hb-blob.hh" + +#include "hb-dispatch.hh" +#include "hb-sanitize.hh" + + +/* + * Casts + */ + +/* StructAtOffset(P,Ofs) returns the struct T& that is placed at memory + * location pointed to by P plus Ofs bytes. */ +template +static inline const Type& StructAtOffset(const void *P, unsigned int offset) +{ return * reinterpret_cast ((const char *) P + offset); } +template +static inline Type& StructAtOffset(void *P, unsigned int offset) +{ return * reinterpret_cast ((char *) P + offset); } +template +static inline const Type& StructAtOffsetUnaligned(const void *P, unsigned int offset) +{ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-align" + return * reinterpret_cast ((const char *) P + offset); +#pragma GCC diagnostic pop +} +template +static inline Type& StructAtOffsetUnaligned(void *P, unsigned int offset) +{ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-align" + return * reinterpret_cast ((char *) P + offset); +#pragma GCC diagnostic pop +} + +/* StructAfter(X) returns the struct T& that is placed after X. + * Works with X of variable size also. X must implement get_size() */ +template +static inline const Type& StructAfter(const TObject &X) +{ return StructAtOffset(&X, X.get_size()); } +template +static inline Type& StructAfter(TObject &X) +{ return StructAtOffset(&X, X.get_size()); } + + +/* + * Size checking + */ + +/* Size signifying variable-sized array */ +#ifndef HB_VAR_ARRAY +#define HB_VAR_ARRAY 1 +#endif + +/* Check _assertion in a method environment */ +#define _DEFINE_INSTANCE_ASSERTION1(_line, _assertion) \ + void _instance_assertion_on_line_##_line () const \ + { static_assert ((_assertion), ""); } +# define _DEFINE_INSTANCE_ASSERTION0(_line, _assertion) _DEFINE_INSTANCE_ASSERTION1 (_line, _assertion) +# define DEFINE_INSTANCE_ASSERTION(_assertion) _DEFINE_INSTANCE_ASSERTION0 (__LINE__, _assertion) + +/* Check that _code compiles in a method environment */ +#define _DEFINE_COMPILES_ASSERTION1(_line, _code) \ + void _compiles_assertion_on_line_##_line () const \ + { _code; } +# define _DEFINE_COMPILES_ASSERTION0(_line, _code) _DEFINE_COMPILES_ASSERTION1 (_line, _code) +# define DEFINE_COMPILES_ASSERTION(_code) _DEFINE_COMPILES_ASSERTION0 (__LINE__, _code) + + +#define DEFINE_SIZE_STATIC(size) \ + DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size)) \ + unsigned int get_size () const { return (size); } \ + static constexpr unsigned null_size = (size); \ + static constexpr unsigned min_size = (size); \ + static constexpr unsigned static_size = (size) + +#define DEFINE_SIZE_UNION(size, _member) \ + DEFINE_COMPILES_ASSERTION ((void) this->u._member.static_size) \ + DEFINE_INSTANCE_ASSERTION (sizeof(this->u._member) == (size)) \ + static constexpr unsigned null_size = (size); \ + static constexpr unsigned min_size = (size) + +#define DEFINE_SIZE_MIN(size) \ + DEFINE_INSTANCE_ASSERTION (sizeof (*this) >= (size)) \ + static constexpr unsigned null_size = (size); \ + static constexpr unsigned min_size = (size) + +#define DEFINE_SIZE_UNBOUNDED(size) \ + DEFINE_INSTANCE_ASSERTION (sizeof (*this) >= (size)) \ + static constexpr unsigned min_size = (size) + +#define DEFINE_SIZE_ARRAY(size, array) \ + DEFINE_COMPILES_ASSERTION ((void) (array)[0].static_size) \ + DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + (HB_VAR_ARRAY+0) * sizeof ((array)[0])) \ + static constexpr unsigned null_size = (size); \ + static constexpr unsigned min_size = (size) + +#define DEFINE_SIZE_ARRAY_SIZED(size, array) \ + unsigned int get_size () const { return (size - (array).min_size + (array).get_size ()); } \ + DEFINE_SIZE_ARRAY(size, array) + + + +/* + * Lazy loaders. + * + * The lazy-loaders are thread-safe pointer-like objects that create their + * instead on-demand. They also support access to a "data" object that is + * necessary for creating their instance. The data object, if specified, + * is accessed via pointer math, located at a location before the position + * of the loader itself. This avoids having to store a pointer to data + * for every lazy-loader. Multiple lazy-loaders can access the same data. + */ + +template +struct hb_data_wrapper_t +{ + static_assert (WheresData > 0, ""); + + Data * get_data () const + { return *(((Data **) (void *) this) - WheresData); } + + bool is_inert () const { return !get_data (); } + + template + Stored * call_create () const { return Subclass::create (get_data ()); } +}; +template <> +struct hb_data_wrapper_t +{ + bool is_inert () const { return false; } + + template + Stored * call_create () const { return Funcs::create (); } +}; + +template struct hb_non_void_t { typedef T1 value; }; +template struct hb_non_void_t { typedef T2 value; }; + +template +struct hb_lazy_loader_t : hb_data_wrapper_t +{ + typedef typename hb_non_void_t + >::value Funcs; + + hb_lazy_loader_t () = default; + hb_lazy_loader_t (const hb_lazy_loader_t &other) = delete; + + void init0 () {} /* Init, when memory is already set to 0. No-op for us. */ + void init () { instance.set_relaxed (nullptr); } + void fini () { do_destroy (instance.get_acquire ()); init (); } + + void free_instance () + { + retry: + Stored *p = instance.get_acquire (); + if (unlikely (p && !cmpexch (p, nullptr))) + goto retry; + do_destroy (p); + } + + static void do_destroy (Stored *p) + { + if (p && p != const_cast (Funcs::get_null ())) + Funcs::destroy (p); + } + + const Returned * operator -> () const { return get (); } + template + const U & operator * () const { return *get (); } + explicit operator bool () const + { return get_stored () != Funcs::get_null (); } + template operator const C * () const { return get (); } + + Stored * get_stored () const + { + retry: + Stored *p = this->instance.get_acquire (); + if (unlikely (!p)) + { + if (unlikely (this->is_inert ())) + return const_cast (Funcs::get_null ()); + + p = this->template call_create (); + if (unlikely (!p)) + p = const_cast (Funcs::get_null ()); + + if (unlikely (!cmpexch (nullptr, p))) + { + do_destroy (p); + goto retry; + } + } + return p; + } + Stored * get_stored_relaxed () const + { + return this->instance.get_relaxed (); + } + + bool cmpexch (Stored *current, Stored *value) const + { + /* This function can only be safely called directly if no + * other thread is accessing. */ + return this->instance.cmpexch (current, value); + } + + const Returned * get () const { return Funcs::convert (get_stored ()); } + const Returned * get_relaxed () const { return Funcs::convert (get_stored_relaxed ()); } + Returned * get_unconst () const { return const_cast (Funcs::convert (get_stored ())); } + + /* To be possibly overloaded by subclasses. */ + static Returned* convert (Stored *p) { return p; } + + /* By default null/init/fini the object. */ + static const Stored* get_null () { return &Null (Stored); } + static Stored *create (Data *data) + { + Stored *p = (Stored *) hb_calloc (1, sizeof (Stored)); + if (likely (p)) + p = new (p) Stored (data); + return p; + } + static Stored *create () + { + Stored *p = (Stored *) hb_calloc (1, sizeof (Stored)); + if (likely (p)) + p = new (p) Stored (); + return p; + } + static void destroy (Stored *p) + { + p->~Stored (); + hb_free (p); + } + + private: + /* Must only have one pointer. */ + hb_atomic_ptr_t instance; +}; + +/* Specializations. */ + +template +struct hb_face_lazy_loader_t : hb_lazy_loader_t, + hb_face_t, WheresFace> +{ + // Hack; have them here for API parity with hb_table_lazy_loader_t + hb_blob_t *get_blob () { return this->get ()->get_blob (); } +}; + +template +struct hb_table_lazy_loader_t : hb_lazy_loader_t, + hb_face_t, WheresFace, + hb_blob_t> +{ + static hb_blob_t *create (hb_face_t *face) + { + hb_sanitize_context_t c; + if (core) + c.set_num_glyphs (0); // So we don't recurse ad infinitum, or doesn't need num_glyphs + return c.reference_table (face); + } + static void destroy (hb_blob_t *p) { hb_blob_destroy (p); } + + static const hb_blob_t *get_null () + { return hb_blob_get_empty (); } + + static const T* convert (const hb_blob_t *blob) + { return blob->as (); } + + hb_blob_t* get_blob () const { return this->get_stored (); } +}; + +#define HB_DEFINE_TYPE_FUNCS_LAZY_LOADER_T(Type) \ + template \ + struct hb_##Type##_funcs_lazy_loader_t : hb_lazy_loader_t \ + { \ + static void destroy (hb_##Type##_funcs_t *p) \ + { hb_##Type##_funcs_destroy (p); } \ + static const hb_##Type##_funcs_t *get_null () \ + { return hb_##Type##_funcs_get_empty (); } \ + } + +HB_DEFINE_TYPE_FUNCS_LAZY_LOADER_T (font); +HB_DEFINE_TYPE_FUNCS_LAZY_LOADER_T (unicode); +HB_DEFINE_TYPE_FUNCS_LAZY_LOADER_T (draw); +HB_DEFINE_TYPE_FUNCS_LAZY_LOADER_T (paint); + +#undef HB_DEFINE_TYPE_FUNCS_LAZY_LOADER_T + + +#endif /* HB_MACHINERY_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-map.cc b/src/3rdparty/harfbuzz-ng/src/hb-map.cc new file mode 100644 index 00000000000..0dc9246f129 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-map.cc @@ -0,0 +1,419 @@ +/* + * 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): Behdad Esfahbod + */ + +#include "hb-map.hh" + + +/** + * SECTION:hb-map + * @title: hb-map + * @short_description: Object representing integer to integer mapping + * @include: hb.h + * + * Map objects are integer-to-integer hash-maps. Currently they are + * not used in the HarfBuzz public API, but are provided for client's + * use if desired. + **/ + + +/** + * hb_map_create: + * + * Creates a new, initially empty map. + * + * Return value: (transfer full): The new #hb_map_t + * + * Since: 1.7.7 + **/ +hb_map_t * +hb_map_create () +{ + hb_map_t *map; + + if (!(map = hb_object_create ())) + return hb_map_get_empty (); + + return map; +} + +/** + * hb_map_get_empty: + * + * Fetches the singleton empty #hb_map_t. + * + * Return value: (transfer full): The empty #hb_map_t + * + * Since: 1.7.7 + **/ +hb_map_t * +hb_map_get_empty () +{ + return const_cast (&Null (hb_map_t)); +} + +/** + * hb_map_reference: (skip) + * @map: A map + * + * Increases the reference count on a map. + * + * Return value: (transfer full): The map + * + * Since: 1.7.7 + **/ +hb_map_t * +hb_map_reference (hb_map_t *map) +{ + return hb_object_reference (map); +} + +/** + * hb_map_destroy: (skip) + * @map: A map + * + * Decreases the reference count on a map. When + * the reference count reaches zero, the map is + * destroyed, freeing all memory. + * + * Since: 1.7.7 + **/ +void +hb_map_destroy (hb_map_t *map) +{ + if (!hb_object_destroy (map)) return; + + hb_free (map); +} + +/** + * hb_map_set_user_data: (skip) + * @map: A map + * @key: The user-data key to set + * @data: A pointer to the user data to set + * @destroy: (nullable): A callback to call when @data is not needed anymore + * @replace: Whether to replace an existing data with the same key + * + * Attaches a user-data key/data pair to the specified map. + * + * Return value: `true` if success, `false` otherwise + * + * Since: 1.7.7 + **/ +hb_bool_t +hb_map_set_user_data (hb_map_t *map, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (map, key, data, destroy, replace); +} + +/** + * hb_map_get_user_data: (skip) + * @map: A map + * @key: The user-data key to query + * + * Fetches the user data associated with the specified key, + * attached to the specified map. + * + * Return value: (transfer none): A pointer to the user data + * + * Since: 1.7.7 + **/ +void * +hb_map_get_user_data (const hb_map_t *map, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (map, key); +} + + +/** + * hb_map_allocation_successful: + * @map: A map + * + * Tests whether memory allocation for a set was successful. + * + * Return value: `true` if allocation succeeded, `false` otherwise + * + * Since: 1.7.7 + **/ +hb_bool_t +hb_map_allocation_successful (const hb_map_t *map) +{ + return map->successful; +} + +/** + * hb_map_copy: + * @map: A map + * + * Allocate a copy of @map. + * + * Return value: (transfer full): Newly-allocated map. + * + * Since: 4.4.0 + **/ +hb_map_t * +hb_map_copy (const hb_map_t *map) +{ + hb_map_t *copy = hb_map_create (); + if (unlikely (copy->in_error ())) + return hb_map_get_empty (); + + *copy = *map; + return copy; +} + +/** + * hb_map_set: + * @map: A map + * @key: The key to store in the map + * @value: The value to store for @key + * + * Stores @key:@value in the map. + * + * Since: 1.7.7 + **/ +void +hb_map_set (hb_map_t *map, + hb_codepoint_t key, + hb_codepoint_t value) +{ + /* Immutable-safe. */ + map->set (key, value); +} + +/** + * hb_map_get: + * @map: A map + * @key: The key to query + * + * Fetches the value stored for @key in @map. + * + * Since: 1.7.7 + **/ +hb_codepoint_t +hb_map_get (const hb_map_t *map, + hb_codepoint_t key) +{ + return map->get (key); +} + +/** + * hb_map_del: + * @map: A map + * @key: The key to delete + * + * Removes @key and its stored value from @map. + * + * Since: 1.7.7 + **/ +void +hb_map_del (hb_map_t *map, + hb_codepoint_t key) +{ + /* Immutable-safe. */ + map->del (key); +} + +/** + * hb_map_has: + * @map: A map + * @key: The key to query + * + * Tests whether @key is an element of @map. + * + * Return value: `true` if @key is found in @map, `false` otherwise + * + * Since: 1.7.7 + **/ +hb_bool_t +hb_map_has (const hb_map_t *map, + hb_codepoint_t key) +{ + return map->has (key); +} + + +/** + * hb_map_clear: + * @map: A map + * + * Clears out the contents of @map. + * + * Since: 1.7.7 + **/ +void +hb_map_clear (hb_map_t *map) +{ + return map->clear (); +} + +/** + * hb_map_is_empty: + * @map: A map + * + * Tests whether @map is empty (contains no elements). + * + * Return value: `true` if @map is empty + * + * Since: 1.7.7 + **/ +hb_bool_t +hb_map_is_empty (const hb_map_t *map) +{ + return map->is_empty (); +} + +/** + * hb_map_get_population: + * @map: A map + * + * Returns the number of key-value pairs in the map. + * + * Return value: The population of @map + * + * Since: 1.7.7 + **/ +unsigned int +hb_map_get_population (const hb_map_t *map) +{ + return map->get_population (); +} + +/** + * hb_map_is_equal: + * @map: A map + * @other: Another map + * + * Tests whether @map and @other are equal (contain the same + * elements). + * + * Return value: `true` if the two maps are equal, `false` otherwise. + * + * Since: 4.3.0 + **/ +hb_bool_t +hb_map_is_equal (const hb_map_t *map, + const hb_map_t *other) +{ + return map->is_equal (*other); +} + +/** + * hb_map_hash: + * @map: A map + * + * Creates a hash representing @map. + * + * Return value: + * A hash of @map. + * + * Since: 4.4.0 + **/ +unsigned int +hb_map_hash (const hb_map_t *map) +{ + return map->hash (); +} + +/** + * hb_map_update: + * @map: A map + * @other: Another map + * + * Add the contents of @other to @map. + * + * Since: 7.0.0 + **/ +HB_EXTERN void +hb_map_update (hb_map_t *map, + const hb_map_t *other) +{ + map->update (*other); +} + +/** + * hb_map_next: + * @map: A map + * @idx: (inout): Iterator internal state + * @key: (out): Key retrieved + * @value: (out): Value retrieved + * + * Fetches the next key/value pair in @map. + * + * Set @idx to -1 to get started. + * + * If the map is modified during iteration, the behavior is undefined. + * + * The order in which the key/values are returned is undefined. + * + * Return value: `true` if there was a next value, `false` otherwise + * + * Since: 7.0.0 + **/ +hb_bool_t +hb_map_next (const hb_map_t *map, + int *idx, + hb_codepoint_t *key, + hb_codepoint_t *value) +{ + return map->next (idx, key, value); +} + +/** + * hb_map_keys: + * @map: A map + * @keys: A set + * + * Add the keys of @map to @keys. + * + * Since: 7.0.0 + **/ +void +hb_map_keys (const hb_map_t *map, + hb_set_t *keys) +{ + hb_copy (map->keys() , *keys); +} + +/** + * hb_map_values: + * @map: A map + * @values: A set + * + * Add the values of @map to @values. + * + * Since: 7.0.0 + **/ +void +hb_map_values (const hb_map_t *map, + hb_set_t *values) +{ + hb_copy (map->values() , *values); +} diff --git a/src/3rdparty/harfbuzz-ng/src/hb-map.h b/src/3rdparty/harfbuzz-ng/src/hb-map.h new file mode 100644 index 00000000000..0ae171714ec --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-map.h @@ -0,0 +1,143 @@ +/* + * 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): Behdad Esfahbod + */ + +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_MAP_H +#define HB_MAP_H + +#include "hb-common.h" +#include "hb-set.h" + +HB_BEGIN_DECLS + + +/** + * HB_MAP_VALUE_INVALID: + * + * Unset #hb_map_t value. + * + * Since: 1.7.7 + */ +#define HB_MAP_VALUE_INVALID HB_CODEPOINT_INVALID + +/** + * hb_map_t: + * + * Data type for holding integer-to-integer hash maps. + * + **/ +typedef struct hb_map_t hb_map_t; + + +HB_EXTERN hb_map_t * +hb_map_create (void); + +HB_EXTERN hb_map_t * +hb_map_get_empty (void); + +HB_EXTERN hb_map_t * +hb_map_reference (hb_map_t *map); + +HB_EXTERN void +hb_map_destroy (hb_map_t *map); + +HB_EXTERN hb_bool_t +hb_map_set_user_data (hb_map_t *map, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + +HB_EXTERN void * +hb_map_get_user_data (const hb_map_t *map, + hb_user_data_key_t *key); + + +/* Returns false if allocation has failed before */ +HB_EXTERN hb_bool_t +hb_map_allocation_successful (const hb_map_t *map); + +HB_EXTERN hb_map_t * +hb_map_copy (const hb_map_t *map); + +HB_EXTERN void +hb_map_clear (hb_map_t *map); + +HB_EXTERN hb_bool_t +hb_map_is_empty (const hb_map_t *map); + +HB_EXTERN unsigned int +hb_map_get_population (const hb_map_t *map); + +HB_EXTERN hb_bool_t +hb_map_is_equal (const hb_map_t *map, + const hb_map_t *other); + +HB_EXTERN unsigned int +hb_map_hash (const hb_map_t *map); + +HB_EXTERN void +hb_map_set (hb_map_t *map, + hb_codepoint_t key, + hb_codepoint_t value); + +HB_EXTERN hb_codepoint_t +hb_map_get (const hb_map_t *map, + hb_codepoint_t key); + +HB_EXTERN void +hb_map_del (hb_map_t *map, + hb_codepoint_t key); + +HB_EXTERN hb_bool_t +hb_map_has (const hb_map_t *map, + hb_codepoint_t key); + +HB_EXTERN void +hb_map_update (hb_map_t *map, + const hb_map_t *other); + +/* Pass -1 in for idx to get started. */ +HB_EXTERN hb_bool_t +hb_map_next (const hb_map_t *map, + int *idx, + hb_codepoint_t *key, + hb_codepoint_t *value); + +HB_EXTERN void +hb_map_keys (const hb_map_t *map, + hb_set_t *keys); + +HB_EXTERN void +hb_map_values (const hb_map_t *map, + hb_set_t *values); + +HB_END_DECLS + +#endif /* HB_MAP_H */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-map.hh b/src/3rdparty/harfbuzz-ng/src/hb-map.hh new file mode 100644 index 00000000000..13d62054c11 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-map.hh @@ -0,0 +1,546 @@ +/* + * 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): Behdad Esfahbod + */ + +#ifndef HB_MAP_HH +#define HB_MAP_HH + +#include "hb.hh" + +#include "hb-set.hh" + + +/* + * hb_hashmap_t + */ + +extern HB_INTERNAL const hb_codepoint_t minus_1; + +template +struct hb_hashmap_t +{ + hb_hashmap_t () { init (); } + ~hb_hashmap_t () { fini (); } + + hb_hashmap_t (const hb_hashmap_t& o) : hb_hashmap_t () { alloc (o.population); hb_copy (o, *this); } + hb_hashmap_t (hb_hashmap_t&& o) : hb_hashmap_t () { hb_swap (*this, o); } + hb_hashmap_t& operator= (const hb_hashmap_t& o) { reset (); alloc (o.population); hb_copy (o, *this); return *this; } + hb_hashmap_t& operator= (hb_hashmap_t&& o) { hb_swap (*this, o); return *this; } + + hb_hashmap_t (std::initializer_list> lst) : hb_hashmap_t () + { + for (auto&& item : lst) + set (item.first, item.second); + } + template + hb_hashmap_t (const Iterable &o) : hb_hashmap_t () + { + auto iter = hb_iter (o); + if (iter.is_random_access_iterator || iter.has_fast_len) + alloc (hb_len (iter)); + hb_copy (iter, *this); + } + + struct item_t + { + K key; + uint32_t is_real_ : 1; + uint32_t is_used_ : 1; + uint32_t hash : 30; + V value; + + item_t () : key (), + is_real_ (false), is_used_ (false), + hash (0), + value () {} + + // Needed for https://github.com/harfbuzz/harfbuzz/issues/4138 + K& get_key () { return key; } + V& get_value () { return value; } + + bool is_used () const { return is_used_; } + void set_used (bool is_used) { is_used_ = is_used; } + void set_real (bool is_real) { is_real_ = is_real; } + bool is_real () const { return is_real_; } + + template + static inline const V& default_value () { return Null(V); }; + template + static inline const V& default_value () + { + static_assert (hb_is_same (V, hb_codepoint_t), ""); + return minus_1; + }; + + bool operator == (const K &o) const { return hb_deref (key) == hb_deref (o); } + bool operator == (const item_t &o) const { return *this == o.key; } + hb_pair_t get_pair() const { return hb_pair_t (key, value); } + hb_pair_t get_pair_ref() { return hb_pair_t (key, value); } + + uint32_t total_hash () const + { return (hash * 31u) + hb_hash (value); } + + static constexpr bool is_trivial = hb_is_trivially_constructible(K) && + hb_is_trivially_destructible(K) && + hb_is_trivially_constructible(V) && + hb_is_trivially_destructible(V); + }; + + hb_object_header_t header; + unsigned int successful : 1; /* Allocations successful */ + unsigned int population : 31; /* Not including tombstones. */ + unsigned int occupancy; /* Including tombstones. */ + unsigned int mask; + unsigned int prime; + unsigned int max_chain_length; + item_t *items; + + friend void swap (hb_hashmap_t& a, hb_hashmap_t& b) + { + if (unlikely (!a.successful || !b.successful)) + return; + unsigned tmp = a.population; + a.population = b.population; + b.population = tmp; + //hb_swap (a.population, b.population); + hb_swap (a.occupancy, b.occupancy); + hb_swap (a.mask, b.mask); + hb_swap (a.prime, b.prime); + hb_swap (a.max_chain_length, b.max_chain_length); + hb_swap (a.items, b.items); + } + void init () + { + hb_object_init (this); + + successful = true; + population = occupancy = 0; + mask = 0; + prime = 0; + max_chain_length = 0; + items = nullptr; + } + void fini () + { + hb_object_fini (this); + + if (likely (items)) + { + unsigned size = mask + 1; + if (!item_t::is_trivial) + for (unsigned i = 0; i < size; i++) + items[i].~item_t (); + hb_free (items); + items = nullptr; + } + population = occupancy = 0; + } + + void reset () + { + successful = true; + clear (); + } + + bool in_error () const { return !successful; } + + bool alloc (unsigned new_population = 0) + { + if (unlikely (!successful)) return false; + + if (new_population != 0 && (new_population + new_population / 2) < mask) return true; + + unsigned int power = hb_bit_storage (hb_max ((unsigned) population, new_population) * 2 + 8); + unsigned int new_size = 1u << power; + item_t *new_items = (item_t *) hb_malloc ((size_t) new_size * sizeof (item_t)); + if (unlikely (!new_items)) + { + successful = false; + return false; + } + if (!item_t::is_trivial) + for (auto &_ : hb_iter (new_items, new_size)) + new (&_) item_t (); + else + hb_memset (new_items, 0, (size_t) new_size * sizeof (item_t)); + + unsigned int old_size = size (); + item_t *old_items = items; + + /* Switch to new, empty, array. */ + population = occupancy = 0; + mask = new_size - 1; + prime = prime_for (power); + max_chain_length = power * 2; + items = new_items; + + /* Insert back old items. */ + for (unsigned int i = 0; i < old_size; i++) + { + if (old_items[i].is_real ()) + { + set_with_hash (std::move (old_items[i].key), + old_items[i].hash, + std::move (old_items[i].value)); + } + if (!item_t::is_trivial) + old_items[i].~item_t (); + } + + hb_free (old_items); + + return true; + } + + template + bool set_with_hash (KK&& key, uint32_t hash, VV&& value, bool overwrite = true) + { + if (unlikely (!successful)) return false; + if (unlikely ((occupancy + occupancy / 2) >= mask && !alloc ())) return false; + + hash &= 0x3FFFFFFF; // We only store lower 30bit of hash + unsigned int tombstone = (unsigned int) -1; + unsigned int i = hash % prime; + unsigned length = 0; + unsigned step = 0; + while (items[i].is_used ()) + { + if ((std::is_integral::value || items[i].hash == hash) && + items[i] == key) + { + if (!overwrite) + return false; + else + break; + } + if (!items[i].is_real () && tombstone == (unsigned) -1) + tombstone = i; + i = (i + ++step) & mask; + length++; + } + + item_t &item = items[tombstone == (unsigned) -1 ? i : tombstone]; + + if (item.is_used ()) + { + occupancy--; + population -= item.is_real (); + } + + item.key = std::forward (key); + item.value = std::forward (value); + item.hash = hash; + item.set_used (true); + item.set_real (true); + + occupancy++; + population++; + + if (unlikely (length > max_chain_length) && occupancy * 8 > mask) + alloc (mask - 8); // This ensures we jump to next larger size + + return true; + } + + template + bool set (const K &key, VV&& value, bool overwrite = true) { return set_with_hash (key, hb_hash (key), std::forward (value), overwrite); } + template + bool set (K &&key, VV&& value, bool overwrite = true) + { + uint32_t hash = hb_hash (key); + return set_with_hash (std::move (key), hash, std::forward (value), overwrite); + } + bool add (const K &key) + { + uint32_t hash = hb_hash (key); + return set_with_hash (key, hash, item_t::default_value ()); + } + + const V& get_with_hash (const K &key, uint32_t hash) const + { + if (!items) return item_t::default_value (); + auto *item = fetch_item (key, hb_hash (key)); + if (item) + return item->value; + return item_t::default_value (); + } + const V& get (const K &key) const + { + if (!items) return item_t::default_value (); + return get_with_hash (key, hb_hash (key)); + } + + void del (const K &key) + { + if (!items) return; + auto *item = fetch_item (key, hb_hash (key)); + if (item) + { + item->set_real (false); + population--; + } + } + + /* Has interface. */ + const V& operator [] (K k) const { return get (k); } + template + bool has (const K &key, VV **vp = nullptr) const + { + if (!items) return false; + auto *item = fetch_item (key, hb_hash (key)); + if (item) + { + if (vp) *vp = std::addressof (item->value); + return true; + } + return false; + } + item_t *fetch_item (const K &key, uint32_t hash) const + { + hash &= 0x3FFFFFFF; // We only store lower 30bit of hash + unsigned int i = hash % prime; + unsigned step = 0; + while (items[i].is_used ()) + { + if ((std::is_integral::value || items[i].hash == hash) && + items[i] == key) + { + if (items[i].is_real ()) + return &items[i]; + else + return nullptr; + } + i = (i + ++step) & mask; + } + return nullptr; + } + /* Projection. */ + const V& operator () (K k) const { return get (k); } + + unsigned size () const { return mask ? mask + 1 : 0; } + + void clear () + { + if (unlikely (!successful)) return; + + for (auto &_ : hb_iter (items, size ())) + { + /* Reconstruct items. */ + _.~item_t (); + new (&_) item_t (); + } + + population = occupancy = 0; + } + + bool is_empty () const { return population == 0; } + explicit operator bool () const { return !is_empty (); } + + uint32_t hash () const + { + return + + iter_items () + | hb_reduce ([] (uint32_t h, const item_t &_) { return h ^ _.total_hash (); }, (uint32_t) 0u) + ; + } + + bool is_equal (const hb_hashmap_t &other) const + { + if (population != other.population) return false; + + for (auto pair : iter ()) + if (other.get (pair.first) != pair.second) + return false; + + return true; + } + bool operator == (const hb_hashmap_t &other) const { return is_equal (other); } + bool operator != (const hb_hashmap_t &other) const { return !is_equal (other); } + + unsigned int get_population () const { return population; } + + void update (const hb_hashmap_t &other) + { + if (unlikely (!successful)) return; + + hb_copy (other, *this); + } + + /* + * Iterator + */ + + auto iter_items () const HB_AUTO_RETURN + ( + + hb_iter (items, this->size ()) + | hb_filter (&item_t::is_real) + ) + auto iter_ref () const HB_AUTO_RETURN + ( + + this->iter_items () + | hb_map (&item_t::get_pair_ref) + ) + auto iter () const HB_AUTO_RETURN + ( + + this->iter_items () + | hb_map (&item_t::get_pair) + ) + auto keys_ref () const HB_AUTO_RETURN + ( + + this->iter_items () + | hb_map (&item_t::get_key) + ) + auto keys () const HB_AUTO_RETURN + ( + + this->keys_ref () + | hb_map (hb_ridentity) + ) + auto values_ref () const HB_AUTO_RETURN + ( + + this->iter_items () + | hb_map (&item_t::get_value) + ) + auto values () const HB_AUTO_RETURN + ( + + this->values_ref () + | hb_map (hb_ridentity) + ) + + /* C iterator. */ + bool next (int *idx, + K *key, + V *value) const + { + unsigned i = (unsigned) (*idx + 1); + + unsigned count = size (); + while (i < count && !items[i].is_real ()) + i++; + + if (i >= count) + { + *idx = -1; + return false; + } + + *key = items[i].key; + *value = items[i].value; + + *idx = (signed) i; + return true; + } + + /* Sink interface. */ + hb_hashmap_t& operator << (const hb_pair_t& v) + { set (v.first, v.second); return *this; } + hb_hashmap_t& operator << (const hb_pair_t& v) + { set (v.first, std::move (v.second)); return *this; } + hb_hashmap_t& operator << (const hb_pair_t& v) + { set (std::move (v.first), v.second); return *this; } + hb_hashmap_t& operator << (const hb_pair_t& v) + { set (std::move (v.first), std::move (v.second)); return *this; } + + static unsigned int prime_for (unsigned int shift) + { + /* Following comment and table copied from glib. */ + /* Each table size has an associated prime modulo (the first prime + * lower than the table size) used to find the initial bucket. Probing + * then works modulo 2^n. The prime modulo is necessary to get a + * good distribution with poor hash functions. + */ + /* Not declaring static to make all kinds of compilers happy... */ + /*static*/ const unsigned int prime_mod [32] = + { + 1, /* For 1 << 0 */ + 2, + 3, + 7, + 13, + 31, + 61, + 127, + 251, + 509, + 1021, + 2039, + 4093, + 8191, + 16381, + 32749, + 65521, /* For 1 << 16 */ + 131071, + 262139, + 524287, + 1048573, + 2097143, + 4194301, + 8388593, + 16777213, + 33554393, + 67108859, + 134217689, + 268435399, + 536870909, + 1073741789, + 2147483647 /* For 1 << 31 */ + }; + + if (unlikely (shift >= ARRAY_LENGTH (prime_mod))) + return prime_mod[ARRAY_LENGTH (prime_mod) - 1]; + + return prime_mod[shift]; + } +}; + +/* + * hb_map_t + */ + +struct hb_map_t : hb_hashmap_t +{ + using hashmap = hb_hashmap_t; + + ~hb_map_t () = default; + hb_map_t () : hashmap () {} + hb_map_t (const hb_map_t &o) : hashmap ((hashmap &) o) {} + hb_map_t (hb_map_t &&o) : hashmap (std::move ((hashmap &) o)) {} + hb_map_t& operator= (const hb_map_t&) = default; + hb_map_t& operator= (hb_map_t&&) = default; + hb_map_t (std::initializer_list lst) : hashmap (lst) {} + template + hb_map_t (const Iterable &o) : hashmap (o) {} +}; + + +#endif /* HB_MAP_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-meta.hh b/src/3rdparty/harfbuzz-ng/src/hb-meta.hh new file mode 100644 index 00000000000..52ff4a84127 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-meta.hh @@ -0,0 +1,238 @@ +/* + * 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): Behdad Esfahbod + */ + +#ifndef HB_META_HH +#define HB_META_HH + +#include "hb.hh" + +#include +#include +#include + + +/* + * C++ template meta-programming & fundamentals used with them. + */ + +/* Void! For when we need a expression-type of void. */ +struct hb_empty_t {}; + +/* https://en.cppreference.com/w/cpp/types/void_t */ +template struct _hb_void_t { typedef void type; }; +template using hb_void_t = typename _hb_void_t::type; + +template struct _hb_head_t { typedef Head type; }; +template using hb_head_t = typename _hb_head_t::type; + +template struct hb_integral_constant { static constexpr T value = v; }; +template using hb_bool_constant = hb_integral_constant; +using hb_true_type = hb_bool_constant; +using hb_false_type = hb_bool_constant; + +/* Static-assert as expression. */ +template struct static_assert_expr; +template <> struct static_assert_expr : hb_false_type {}; +#define static_assert_expr(C) static_assert_expr::value + +/* Basic type SFINAE. */ + +template struct hb_enable_if {}; +template struct hb_enable_if { typedef T type; }; +#define hb_enable_if(Cond) typename hb_enable_if<(Cond)>::type* = nullptr +/* Concepts/Requires alias: */ +#define hb_requires(Cond) hb_enable_if((Cond)) + +template struct hb_is_same : hb_false_type {}; +template struct hb_is_same : hb_true_type {}; +#define hb_is_same(T, T2) hb_is_same::value + +/* Function overloading SFINAE and priority. */ + +#define HB_RETURN(Ret, E) -> hb_head_t { return (E); } +#define HB_AUTO_RETURN(E) -> decltype ((E)) { return (E); } +#define HB_VOID_RETURN(E) -> hb_void_t { (E); } + +template struct hb_priority : hb_priority {}; +template <> struct hb_priority<0> {}; +#define hb_prioritize hb_priority<16> () + +#define HB_FUNCOBJ(x) static_const x HB_UNUSED + + +template struct hb_type_identity_t { typedef T type; }; +template using hb_type_identity = typename hb_type_identity_t::type; + +template static inline T hb_declval (); +#define hb_declval(T) (hb_declval ()) + +template struct hb_match_const : hb_type_identity_t, hb_false_type {}; +template struct hb_match_const : hb_type_identity_t, hb_true_type {}; +template using hb_remove_const = typename hb_match_const::type; + +template struct hb_match_reference : hb_type_identity_t, hb_false_type {}; +template struct hb_match_reference : hb_type_identity_t, hb_true_type {}; +template struct hb_match_reference : hb_type_identity_t, hb_true_type {}; +template using hb_remove_reference = typename hb_match_reference::type; +template auto _hb_try_add_lvalue_reference (hb_priority<1>) -> hb_type_identity; +template auto _hb_try_add_lvalue_reference (hb_priority<0>) -> hb_type_identity; +template using hb_add_lvalue_reference = decltype (_hb_try_add_lvalue_reference (hb_prioritize)); +template auto _hb_try_add_rvalue_reference (hb_priority<1>) -> hb_type_identity; +template auto _hb_try_add_rvalue_reference (hb_priority<0>) -> hb_type_identity; +template using hb_add_rvalue_reference = decltype (_hb_try_add_rvalue_reference (hb_prioritize)); + +template struct hb_match_pointer : hb_type_identity_t, hb_false_type {}; +template struct hb_match_pointer : hb_type_identity_t, hb_true_type {}; +template using hb_remove_pointer = typename hb_match_pointer::type; +template auto _hb_try_add_pointer (hb_priority<1>) -> hb_type_identity*>; +template auto _hb_try_add_pointer (hb_priority<1>) -> hb_type_identity; +template using hb_add_pointer = decltype (_hb_try_add_pointer (hb_prioritize)); + + +template using hb_decay = typename std::decay::type; + +#define hb_is_convertible(From,To) std::is_convertible::value + +template +using hb_is_cr_convertible = hb_bool_constant< + hb_is_same (hb_decay, hb_decay) && + (!std::is_const::value || std::is_const::value) && + (!std::is_reference::value || std::is_const::value || std::is_reference::value) +>; +#define hb_is_cr_convertible(From,To) hb_is_cr_convertible::value + + +struct +{ + template constexpr auto + operator () (T&& v) const HB_AUTO_RETURN (std::forward (v)) + + template constexpr auto + operator () (T *v) const HB_AUTO_RETURN (*v) + + template constexpr auto + operator () (const hb::shared_ptr& v) const HB_AUTO_RETURN (*v) + + template constexpr auto + operator () (hb::shared_ptr& v) const HB_AUTO_RETURN (*v) + + template constexpr auto + operator () (const hb::unique_ptr& v) const HB_AUTO_RETURN (*v) + + template constexpr auto + operator () (hb::unique_ptr& v) const HB_AUTO_RETURN (*v) +} +HB_FUNCOBJ (hb_deref); + +template +struct hb_reference_wrapper +{ + hb_reference_wrapper (T v) : v (v) {} + bool operator == (const hb_reference_wrapper& o) const { return v == o.v; } + bool operator != (const hb_reference_wrapper& o) const { return v != o.v; } + operator T& () { return v; } + T& get () { return v; } + T v; +}; +template +struct hb_reference_wrapper +{ + hb_reference_wrapper (T& v) : v (std::addressof (v)) {} + bool operator == (const hb_reference_wrapper& o) const { return v == o.v; } + bool operator != (const hb_reference_wrapper& o) const { return v != o.v; } + operator T& () { return *v; } + T& get () { return *v; } + T* v; +}; + + +/* Type traits */ + +template struct hb_int_min; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template struct hb_int_min : hb_integral_constant {}; +#define hb_int_min(T) hb_int_min::value +template struct hb_int_max; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +#define hb_int_max(T) hb_int_max::value + +#if defined(__GNUC__) && __GNUC__ < 5 && !defined(__clang__) +#define hb_is_trivially_copyable(T) __has_trivial_copy(T) +#define hb_is_trivially_copy_assignable(T) __has_trivial_assign(T) +#define hb_is_trivially_constructible(T) __has_trivial_constructor(T) +#define hb_is_trivially_copy_constructible(T) __has_trivial_copy_constructor(T) +#define hb_is_trivially_destructible(T) __has_trivial_destructor(T) +#else +#define hb_is_trivially_copyable(T) std::is_trivially_copyable::value +#define hb_is_trivially_copy_assignable(T) std::is_trivially_copy_assignable::value +#define hb_is_trivially_constructible(T) std::is_trivially_constructible::value +#define hb_is_trivially_copy_constructible(T) std::is_trivially_copy_constructible::value +#define hb_is_trivially_destructible(T) std::is_trivially_destructible::value +#endif + +/* Class traits. */ + +#define HB_DELETE_COPY_ASSIGN(TypeName) \ + TypeName(const TypeName&) = delete; \ + void operator=(const TypeName&) = delete +#define HB_DELETE_CREATE_COPY_ASSIGN(TypeName) \ + TypeName() = delete; \ + TypeName(const TypeName&) = delete; \ + void operator=(const TypeName&) = delete + +/* hb_unwrap_type (T) + * If T has no T::type, returns T. Otherwise calls itself on T::type recursively. + */ + +template +struct _hb_unwrap_type : hb_type_identity_t {}; +template +struct _hb_unwrap_type> : _hb_unwrap_type {}; +template +using hb_unwrap_type = _hb_unwrap_type; +#define hb_unwrap_type(T) typename hb_unwrap_type::type + +#endif /* HB_META_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ms-feature-ranges.hh b/src/3rdparty/harfbuzz-ng/src/hb-ms-feature-ranges.hh new file mode 100644 index 00000000000..f7649ab76e4 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-ms-feature-ranges.hh @@ -0,0 +1,232 @@ +/* + * Copyright © 2011,2012,2013 Google, Inc. + * Copyright © 2021 Khaled Hosny + * + * 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): Behdad Esfahbod + */ + +#ifndef HB_MS_FEATURE_RANGES_HH +#define HB_MS_FEATURE_RANGES_HH + +#include "hb.hh" + +/* Variations of this code exist in hb-coretext.cc as well + * as hb-aat-map.cc... */ + +typedef struct hb_ms_feature_t { + uint32_t tag_le; + uint32_t value; +} hb_ms_feature_t; + +typedef struct hb_ms_features_t { + hb_ms_feature_t *features; + uint32_t num_features; +} hb_ms_features_t; + +struct hb_ms_active_feature_t { + hb_ms_feature_t fea; + unsigned int order; + + HB_INTERNAL static int cmp (const void *pa, const void *pb) { + const auto *a = (const hb_ms_active_feature_t *) pa; + const auto *b = (const hb_ms_active_feature_t *) pb; + return a->fea.tag_le < b->fea.tag_le ? -1 : a->fea.tag_le > b->fea.tag_le ? 1 : + a->order < b->order ? -1 : a->order > b->order ? 1 : + a->fea.value < b->fea.value ? -1 : a->fea.value > b->fea.value ? 1 : + 0; + } + bool operator== (const hb_ms_active_feature_t& f) const + { return cmp (this, &f) == 0; } +}; + +struct hb_ms_feature_event_t { + unsigned int index; + bool start; + hb_ms_active_feature_t feature; + + HB_INTERNAL static int cmp (const void *pa, const void *pb) + { + const auto *a = (const hb_ms_feature_event_t *) pa; + const auto *b = (const hb_ms_feature_event_t *) pb; + return a->index < b->index ? -1 : a->index > b->index ? 1 : + a->start < b->start ? -1 : a->start > b->start ? 1 : + hb_ms_active_feature_t::cmp (&a->feature, &b->feature); + } +}; + +struct hb_ms_range_record_t { + hb_ms_features_t features; + unsigned int index_first; /* == start */ + unsigned int index_last; /* == end - 1 */ +}; + +static inline bool +hb_ms_setup_features (const hb_feature_t *features, + unsigned int num_features, + hb_vector_t &feature_records, /* OUT */ + hb_vector_t &range_records /* OUT */) +{ + feature_records.shrink(0); + range_records.shrink(0); + + /* Sort features by start/end events. */ + hb_vector_t feature_events; + for (unsigned int i = 0; i < num_features; i++) + { + hb_ms_active_feature_t feature; + feature.fea.tag_le = hb_uint32_swap (features[i].tag); + feature.fea.value = features[i].value; + feature.order = i; + + hb_ms_feature_event_t *event; + + event = feature_events.push (); + event->index = features[i].start; + event->start = true; + event->feature = feature; + + event = feature_events.push (); + event->index = features[i].end; + event->start = false; + event->feature = feature; + } + feature_events.qsort (); + /* Add a strategic final event. */ + { + hb_ms_active_feature_t feature; + feature.fea.tag_le = 0; + feature.fea.value = 0; + feature.order = num_features + 1; + + auto *event = feature_events.push (); + event->index = 0; /* This value does magic. */ + event->start = false; + event->feature = feature; + } + + /* Scan events and save features for each range. */ + hb_vector_t active_features; + unsigned int last_index = 0; + for (unsigned int i = 0; i < feature_events.length; i++) + { + auto *event = &feature_events[i]; + + if (event->index != last_index) + { + /* Save a snapshot of active features and the range. */ + auto *range = range_records.push (); + auto offset = feature_records.length; + + active_features.qsort (); + for (unsigned int j = 0; j < active_features.length; j++) + { + if (!j || active_features[j].fea.tag_le != feature_records[feature_records.length - 1].tag_le) + { + feature_records.push (active_features[j].fea); + } + else + { + /* Overrides value for existing feature. */ + feature_records[feature_records.length - 1].value = active_features[j].fea.value; + } + } + + /* Will convert to pointer after all is ready, since feature_records.array + * may move as we grow it. */ + range->features.features = reinterpret_cast (offset); + range->features.num_features = feature_records.length - offset; + range->index_first = last_index; + range->index_last = event->index - 1; + + last_index = event->index; + } + + if (event->start) + { + active_features.push (event->feature); + } + else + { + auto *feature = active_features.lsearch (event->feature); + if (feature) + active_features.remove_ordered (feature - active_features.arrayZ); + } + } + + if (!range_records.length) /* No active feature found. */ + num_features = 0; + + /* Fixup the pointers. */ + for (unsigned int i = 0; i < range_records.length; i++) + { + auto *range = &range_records[i]; + range->features.features = (hb_ms_feature_t *) feature_records + reinterpret_cast (range->features.features); + } + + return !!num_features; +} + +static inline void +hb_ms_make_feature_ranges (hb_vector_t &feature_records, + hb_vector_t &range_records, + unsigned int chars_offset, + unsigned int chars_len, + uint16_t *log_clusters, + hb_vector_t &range_features, /* OUT */ + hb_vector_t &range_counts /* OUT */) +{ + range_features.shrink (0); + range_counts.shrink (0); + + auto *last_range = &range_records[0]; + for (unsigned int i = chars_offset; i < chars_len; i++) + { + auto *range = last_range; + while (log_clusters[i] < range->index_first) + range--; + while (log_clusters[i] > range->index_last) + range++; + if (!range_features.length || + &range->features != range_features[range_features.length - 1]) + { + auto **features = range_features.push (); + auto *c = range_counts.push (); + if (unlikely (!features || !c)) + { + range_features.shrink (0); + range_counts.shrink (0); + break; + } + *features = &range->features; + *c = 1; + } + else + { + range_counts[range_counts.length - 1]++; + } + + last_range = range; + } +} + +#endif /* HB_MS_FEATURE_RANGES_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-multimap.hh b/src/3rdparty/harfbuzz-ng/src/hb-multimap.hh new file mode 100644 index 00000000000..0184279c127 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-multimap.hh @@ -0,0 +1,96 @@ +/* + * Copyright © 2022 Behdad Esfahbod + * + * 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. + */ + +#ifndef HB_MULTIMAP_HH +#define HB_MULTIMAP_HH + +#include "hb.hh" +#include "hb-map.hh" +#include "hb-vector.hh" + + +/* + * hb_multimap_t + */ + +struct hb_multimap_t +{ + void add (hb_codepoint_t k, hb_codepoint_t v) + { + hb_vector_t *m; + if (multiples.has (k, &m)) + { + m->push (v); + return; + } + + hb_codepoint_t *old_v; + if (singulars.has (k, &old_v)) + { + hb_codepoint_t old = *old_v; + singulars.del (k); + + multiples.set (k, hb_vector_t {old, v}); + return; + } + + singulars.set (k, v); + } + + hb_array_t get (hb_codepoint_t k) const + { + const hb_codepoint_t *v; + if (singulars.has (k, &v)) + return hb_array (v, 1); + + hb_vector_t *m; + if (multiples.has (k, &m)) + return m->as_array (); + + return hb_array_t (); + } + + bool in_error () const + { + if (singulars.in_error () || multiples.in_error ()) + return true; + for (const auto &m : multiples.values_ref ()) + if (m.in_error ()) + return true; + return false; + } + + void alloc (unsigned size) + { + singulars.alloc (size); + } + + protected: + hb_map_t singulars; + hb_hashmap_t> multiples; +}; + + + +#endif /* HB_MULTIMAP_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-mutex-private.hh b/src/3rdparty/harfbuzz-ng/src/hb-mutex.hh similarity index 54% rename from src/3rdparty/harfbuzz-ng/src/hb-mutex-private.hh rename to src/3rdparty/harfbuzz-ng/src/hb-mutex.hh index 49ed10e08a6..e329d9864f1 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-mutex-private.hh +++ b/src/3rdparty/harfbuzz-ng/src/hb-mutex.hh @@ -29,18 +29,17 @@ * Google Author(s): Behdad Esfahbod */ -#ifndef HB_MUTEX_PRIVATE_HH -#define HB_MUTEX_PRIVATE_HH +#ifndef HB_MUTEX_HH +#define HB_MUTEX_HH -#include "hb-private.hh" +#include "hb.hh" /* mutex */ /* We need external help for these */ -#if defined(HB_MUTEX_IMPL_INIT) \ - && defined(hb_mutex_impl_init) \ +#if defined(hb_mutex_impl_init) \ && defined(hb_mutex_impl_lock) \ && defined(hb_mutex_impl_unlock) \ && defined(hb_mutex_impl_finish) @@ -48,12 +47,20 @@ /* Defined externally, i.e. in config.h; must have typedef'ed hb_mutex_impl_t as well. */ -#elif !defined(HB_NO_MT) && (defined(_WIN32) || defined(__CYGWIN__)) +#elif !defined(HB_NO_MT) && !defined(HB_MUTEX_IMPL_STD_MUTEX) && (defined(HAVE_PTHREAD) || defined(__APPLE__)) + +#include +typedef pthread_mutex_t hb_mutex_impl_t; +#define hb_mutex_impl_init(M) pthread_mutex_init (M, nullptr) +#define hb_mutex_impl_lock(M) pthread_mutex_lock (M) +#define hb_mutex_impl_unlock(M) pthread_mutex_unlock (M) +#define hb_mutex_impl_finish(M) pthread_mutex_destroy (M) + + +#elif !defined(HB_NO_MT) && !defined(HB_MUTEX_IMPL_STD_MUTEX) && defined(_WIN32) -#include typedef CRITICAL_SECTION hb_mutex_impl_t; -#define HB_MUTEX_IMPL_INIT {0} -#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_PC_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP) +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) #define hb_mutex_impl_init(M) InitializeCriticalSectionEx (M, 0, 0) #else #define hb_mutex_impl_init(M) InitializeCriticalSection (M) @@ -63,57 +70,19 @@ typedef CRITICAL_SECTION hb_mutex_impl_t; #define hb_mutex_impl_finish(M) DeleteCriticalSection (M) -#elif !defined(HB_NO_MT) && (defined(HAVE_PTHREAD) || defined(__APPLE__)) - -#include -typedef pthread_mutex_t hb_mutex_impl_t; -#define HB_MUTEX_IMPL_INIT PTHREAD_MUTEX_INITIALIZER -#define hb_mutex_impl_init(M) pthread_mutex_init (M, nullptr) -#define hb_mutex_impl_lock(M) pthread_mutex_lock (M) -#define hb_mutex_impl_unlock(M) pthread_mutex_unlock (M) -#define hb_mutex_impl_finish(M) pthread_mutex_destroy (M) - - -#elif !defined(HB_NO_MT) && defined(HAVE_INTEL_ATOMIC_PRIMITIVES) - -#if defined(HAVE_SCHED_H) && defined(HAVE_SCHED_YIELD) -# include -# define HB_SCHED_YIELD() sched_yield () -#else -# define HB_SCHED_YIELD() HB_STMT_START {} HB_STMT_END -#endif - -/* This actually is not a totally awful implementation. */ -typedef volatile int hb_mutex_impl_t; -#define HB_MUTEX_IMPL_INIT 0 -#define hb_mutex_impl_init(M) *(M) = 0 -#define hb_mutex_impl_lock(M) HB_STMT_START { while (__sync_lock_test_and_set((M), 1)) HB_SCHED_YIELD (); } HB_STMT_END -#define hb_mutex_impl_unlock(M) __sync_lock_release (M) -#define hb_mutex_impl_finish(M) HB_STMT_START {} HB_STMT_END - - #elif !defined(HB_NO_MT) -#if defined(HAVE_SCHED_H) && defined(HAVE_SCHED_YIELD) -# include -# define HB_SCHED_YIELD() sched_yield () -#else -# define HB_SCHED_YIELD() HB_STMT_START {} HB_STMT_END -#endif - -#define HB_MUTEX_INT_NIL 1 /* Warn that fallback implementation is in use. */ -typedef volatile int hb_mutex_impl_t; -#define HB_MUTEX_IMPL_INIT 0 -#define hb_mutex_impl_init(M) *(M) = 0 -#define hb_mutex_impl_lock(M) HB_STMT_START { while (*(M)) HB_SCHED_YIELD (); (*(M))++; } HB_STMT_END -#define hb_mutex_impl_unlock(M) (*(M))--; -#define hb_mutex_impl_finish(M) HB_STMT_START {} HB_STMT_END +#include +typedef std::mutex hb_mutex_impl_t; +#define hb_mutex_impl_init(M) HB_STMT_START { new (M) hb_mutex_impl_t; } HB_STMT_END +#define hb_mutex_impl_lock(M) (M)->lock () +#define hb_mutex_impl_unlock(M) (M)->unlock () +#define hb_mutex_impl_finish(M) HB_STMT_START { (M)->~hb_mutex_impl_t(); } HB_STMT_END -#else /* HB_NO_MT */ +#else /* defined(HB_NO_MT) */ typedef int hb_mutex_impl_t; -#define HB_MUTEX_IMPL_INIT 0 #define hb_mutex_impl_init(M) HB_STMT_START {} HB_STMT_END #define hb_mutex_impl_lock(M) HB_STMT_START {} HB_STMT_END #define hb_mutex_impl_unlock(M) HB_STMT_START {} HB_STMT_END @@ -123,19 +92,31 @@ typedef int hb_mutex_impl_t; #endif -#define HB_MUTEX_INIT {HB_MUTEX_IMPL_INIT} - struct hb_mutex_t { - /* TODO Add tracing. */ + /* Create space for, but do not initialize m. */ + alignas(hb_mutex_impl_t) char m[sizeof (hb_mutex_impl_t)]; - hb_mutex_impl_t m; + hb_mutex_t () { init (); } + ~hb_mutex_t () { fini (); } - inline void init (void) { hb_mutex_impl_init (&m); } - inline void lock (void) { hb_mutex_impl_lock (&m); } - inline void unlock (void) { hb_mutex_impl_unlock (&m); } - inline void finish (void) { hb_mutex_impl_finish (&m); } +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-align" + void init () { hb_mutex_impl_init ((hb_mutex_impl_t *) m); } + void lock () { hb_mutex_impl_lock ((hb_mutex_impl_t *) m); } + void unlock () { hb_mutex_impl_unlock ((hb_mutex_impl_t *) m); } + void fini () { hb_mutex_impl_finish ((hb_mutex_impl_t *) m); } +#pragma GCC diagnostic pop +}; + +struct hb_lock_t +{ + hb_lock_t (hb_mutex_t &mutex_) : mutex (&mutex_) { mutex->lock (); } + hb_lock_t (hb_mutex_t *mutex_) : mutex (mutex_) { if (mutex) mutex->lock (); } + ~hb_lock_t () { if (mutex) mutex->unlock (); } + private: + hb_mutex_t *mutex; }; -#endif /* HB_MUTEX_PRIVATE_HH */ +#endif /* HB_MUTEX_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-null.hh b/src/3rdparty/harfbuzz-ng/src/hb-null.hh new file mode 100644 index 00000000000..854485d3df8 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-null.hh @@ -0,0 +1,226 @@ +/* + * 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): Behdad Esfahbod + */ + +#ifndef HB_NULL_HH +#define HB_NULL_HH + +#include "hb.hh" +#include "hb-meta.hh" + + +/* + * Static pools + */ + +/* Global nul-content Null pool. Enlarge as necessary. */ + +#define HB_NULL_POOL_SIZE 640 + +template +struct _hb_has_min_size : hb_false_type {}; +template +struct _hb_has_min_size> + : hb_true_type {}; +template +using hb_has_min_size = _hb_has_min_size; +#define hb_has_min_size(T) hb_has_min_size::value + +template +struct _hb_has_null_size : hb_false_type {}; +template +struct _hb_has_null_size> + : hb_true_type {}; +template +using hb_has_null_size = _hb_has_null_size; +#define hb_has_null_size(T) hb_has_null_size::value + +/* Use SFINAE to sniff whether T has min_size; in which case return the larger + * of sizeof(T) and T::null_size, otherwise return sizeof(T). + * + * The main purpose of this is to let structs communicate that they are not nullable, + * by defining min_size but *not* null_size. */ + +/* The hard way... + * https://stackoverflow.com/questions/7776448/sfinae-tried-with-bool-gives-compiler-error-template-argument-tvalue-invol + */ + +template +struct _hb_null_size : hb_integral_constant {}; +template +struct _hb_null_size> + : hb_integral_constant T::null_size ? sizeof (T) : T::null_size)> {}; +template +using hb_null_size = _hb_null_size; +#define hb_null_size(T) hb_null_size::value + +/* These doesn't belong here, but since is copy/paste from above, put it here. */ + +/* hb_static_size (T) + * Returns T::static_size if T::min_size is defined, or sizeof (T) otherwise. */ + +template +struct _hb_static_size : hb_integral_constant {}; +template +struct _hb_static_size> : hb_integral_constant {}; +template +using hb_static_size = _hb_static_size; +#define hb_static_size(T) hb_static_size::value + +template +struct _hb_min_size : hb_integral_constant {}; +template +struct _hb_min_size> : hb_integral_constant {}; +template +using hb_min_size = _hb_min_size; +#define hb_min_size(T) hb_min_size::value + + +/* + * Null() + */ + +extern HB_INTERNAL +uint64_t const _hb_NullPool[(HB_NULL_POOL_SIZE + sizeof (uint64_t) - 1) / sizeof (uint64_t)]; + +/* Generic nul-content Null objects. */ +template +struct Null { + static Type const & get_null () + { + static_assert (hb_null_size (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE."); + return *reinterpret_cast (_hb_NullPool); + } +}; +template +struct NullHelper +{ + typedef hb_remove_const> Type; + static const Type & get_null () { return Null::get_null (); } +}; +#define Null(Type) NullHelper::get_null () + +/* Specializations for arbitrary-content Null objects expressed in bytes. */ +#define DECLARE_NULL_NAMESPACE_BYTES(Namespace, Type) \ + } /* Close namespace. */ \ + extern HB_INTERNAL const unsigned char _hb_Null_##Namespace##_##Type[hb_null_size (Namespace::Type)]; \ + template <> \ + struct Null { \ + static Namespace::Type const & get_null () { \ + return *reinterpret_cast (_hb_Null_##Namespace##_##Type); \ + } \ + }; \ + namespace Namespace { \ + static_assert (true, "") /* Require semicolon after. */ +#define DECLARE_NULL_NAMESPACE_BYTES_TEMPLATE1(Namespace, Type, Size) \ + } /* Close namespace. */ \ + extern HB_INTERNAL const unsigned char _hb_Null_##Namespace##_##Type[Size]; \ + template \ + struct Null> { \ + static Namespace::Type const & get_null () { \ + return *reinterpret_cast *> (_hb_Null_##Namespace##_##Type); \ + } \ + }; \ + namespace Namespace { \ + static_assert (true, "") /* Require semicolon after. */ +#define DEFINE_NULL_NAMESPACE_BYTES(Namespace, Type) \ + const unsigned char _hb_Null_##Namespace##_##Type[sizeof (_hb_Null_##Namespace##_##Type)] + +/* Specializations for arbitrary-content Null objects expressed as struct initializer. */ +#define DECLARE_NULL_INSTANCE(Type) \ + extern HB_INTERNAL const Type _hb_Null_##Type; \ + template <> \ + struct Null { \ + static Type const & get_null () { \ + return _hb_Null_##Type; \ + } \ + }; \ + static_assert (true, "") /* Require semicolon after. */ +#define DEFINE_NULL_INSTANCE(Type) \ + const Type _hb_Null_##Type + +/* Global writable pool. Enlarge as necessary. */ + +/* To be fully correct, CrapPool must be thread_local. However, we do not rely on CrapPool + * for correct operation. It only exist to catch and divert program logic bugs instead of + * causing bad memory access. So, races there are not actually introducing incorrectness + * in the code. Has ~12kb binary size overhead to have it, also clang build fails with it. */ +extern HB_INTERNAL +/*thread_local*/ uint64_t _hb_CrapPool[(HB_NULL_POOL_SIZE + sizeof (uint64_t) - 1) / sizeof (uint64_t)]; + +/* CRAP pool: Common Region for Access Protection. */ +template +static inline Type& Crap () { + static_assert (hb_null_size (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE."); + Type *obj = reinterpret_cast (_hb_CrapPool); + memcpy (obj, std::addressof (Null (Type)), sizeof (*obj)); + return *obj; +} +template +struct CrapHelper +{ + typedef hb_remove_const> Type; + static Type & get_crap () { return Crap (); } +}; +#define Crap(Type) CrapHelper::get_crap () + +template +struct CrapOrNullHelper { + static Type & get () { return Crap (Type); } +}; +template +struct CrapOrNullHelper { + static const Type & get () { return Null (Type); } +}; +#define CrapOrNull(Type) CrapOrNullHelper::get () + + +/* + * hb_nonnull_ptr_t + */ + +template +struct hb_nonnull_ptr_t +{ + typedef hb_remove_pointer

T; + + hb_nonnull_ptr_t (T *v_ = nullptr) : v (v_) {} + T * operator = (T *v_) { return v = v_; } + T * operator -> () const { return get (); } + T & operator * () const { return *get (); } + T ** operator & () const { return std::addressof (v); } + /* Only auto-cast to const types. */ + template operator const C * () const { return get (); } + operator const char * () const { return (const char *) get (); } + T * get () const { return v ? v : const_cast (std::addressof (Null (T))); } + T * get_raw () const { return v; } + + private: + T *v; +}; + + +#endif /* HB_NULL_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-number-parser.hh b/src/3rdparty/harfbuzz-ng/src/hb-number-parser.hh new file mode 100644 index 00000000000..1a9dbba6dd3 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-number-parser.hh @@ -0,0 +1,237 @@ + +#line 1 "hb-number-parser.rl" +/* + * Copyright © 2019 Ebrahim Byagowi + * + * 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. + * + */ + +#ifndef HB_NUMBER_PARSER_HH +#define HB_NUMBER_PARSER_HH + +#include "hb.hh" + + +#line 35 "hb-number-parser.hh" +static const unsigned char _double_parser_trans_keys[] = { + 0u, 0u, 43u, 57u, 46u, 57u, 48u, 57u, 43u, 57u, 48u, 57u, 48u, 101u, 48u, 57u, + 46u, 101u, 0 +}; + +static const char _double_parser_key_spans[] = { + 0, 15, 12, 10, 15, 10, 54, 10, + 56 +}; + +static const unsigned char _double_parser_index_offsets[] = { + 0, 0, 16, 29, 40, 56, 67, 122, + 133 +}; + +static const char _double_parser_indicies[] = { + 0, 1, 2, 3, 1, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 1, 3, 1, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 1, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 1, 6, 1, 7, 1, 1, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 1, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 1, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 9, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 9, 1, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 1, 3, 1, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 9, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 9, 1, 0 +}; + +static const char _double_parser_trans_targs[] = { + 2, 0, 2, 3, 8, 6, 5, 5, + 7, 4 +}; + +static const char _double_parser_trans_actions[] = { + 0, 0, 1, 0, 2, 3, 0, 4, + 5, 0 +}; + +static const int double_parser_start = 1; +static const int double_parser_first_final = 6; +static const int double_parser_error = 0; + +static const int double_parser_en_main = 1; + + +#line 68 "hb-number-parser.rl" + + +/* Works only for n < 512 */ +static inline double +_pow10 (unsigned exponent) +{ + static const double _powers_of_10[] = + { + 1.0e+256, + 1.0e+128, + 1.0e+64, + 1.0e+32, + 1.0e+16, + 1.0e+8, + 10000., + 100., + 10. + }; + unsigned mask = 1 << (ARRAY_LENGTH (_powers_of_10) - 1); + double result = 1; + for (const double *power = _powers_of_10; mask; ++power, mask >>= 1) + if (exponent & mask) result *= *power; + return result; +} + +/* a variant of strtod that also gets end of buffer in its second argument */ +static inline double +strtod_rl (const char *p, const char **end_ptr /* IN/OUT */) +{ + double value = 0; + double frac = 0; + double frac_count = 0; + unsigned exp = 0; + bool neg = false, exp_neg = false, exp_overflow = false; + const unsigned long long MAX_FRACT = 0xFFFFFFFFFFFFFull; /* 2^52-1 */ + const unsigned MAX_EXP = 0x7FFu; /* 2^11-1 */ + + const char *pe = *end_ptr; + while (p < pe && ISSPACE (*p)) + p++; + + int cs; + +#line 139 "hb-number-parser.hh" + { + cs = double_parser_start; + } + +#line 144 "hb-number-parser.hh" + { + int _slen; + int _trans; + const unsigned char *_keys; + const char *_inds; + if ( p == pe ) + goto _test_eof; + if ( cs == 0 ) + goto _out; +_resume: + _keys = _double_parser_trans_keys + (cs<<1); + _inds = _double_parser_indicies + _double_parser_index_offsets[cs]; + + _slen = _double_parser_key_spans[cs]; + _trans = _inds[ _slen > 0 && _keys[0] <=(*p) && + (*p) <= _keys[1] ? + (*p) - _keys[0] : _slen ]; + + cs = _double_parser_trans_targs[_trans]; + + if ( _double_parser_trans_actions[_trans] == 0 ) + goto _again; + + switch ( _double_parser_trans_actions[_trans] ) { + case 1: +#line 37 "hb-number-parser.rl" + { neg = true; } + break; + case 4: +#line 38 "hb-number-parser.rl" + { exp_neg = true; } + break; + case 2: +#line 40 "hb-number-parser.rl" + { + value = value * 10. + ((*p) - '0'); +} + break; + case 3: +#line 43 "hb-number-parser.rl" + { + if (likely (frac <= MAX_FRACT / 10)) + { + frac = frac * 10. + ((*p) - '0'); + ++frac_count; + } +} + break; + case 5: +#line 50 "hb-number-parser.rl" + { + if (likely (exp * 10 + ((*p) - '0') <= MAX_EXP)) + exp = exp * 10 + ((*p) - '0'); + else + exp_overflow = true; +} + break; +#line 202 "hb-number-parser.hh" + } + +_again: + if ( cs == 0 ) + goto _out; + if ( ++p != pe ) + goto _resume; + _test_eof: {} + _out: {} + } + +#line 113 "hb-number-parser.rl" + + + *end_ptr = p; + + if (frac_count) value += frac / _pow10 (frac_count); + if (neg) value *= -1.; + + if (unlikely (exp_overflow)) + { + if (value == 0) return value; + if (exp_neg) return neg ? -DBL_MIN : DBL_MIN; + else return neg ? -DBL_MAX : DBL_MAX; + } + + if (exp) + { + if (exp_neg) value /= _pow10 (exp); + else value *= _pow10 (exp); + } + + return value; +} + +#endif /* HB_NUMBER_PARSER_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-number.cc b/src/3rdparty/harfbuzz-ng/src/hb-number.cc new file mode 100644 index 00000000000..c52b284e1d2 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-number.cc @@ -0,0 +1,79 @@ +/* + * Copyright © 2019 Ebrahim Byagowi + * + * 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. + * + */ + +#include "hb.hh" +#include "hb-number.hh" +#include "hb-number-parser.hh" + +template +static bool +_parse_number (const char **pp, const char *end, T *pv, + bool whole_buffer, Func f) +{ + char buf[32]; + unsigned len = hb_min (ARRAY_LENGTH (buf) - 1, (unsigned) (end - *pp)); + strncpy (buf, *pp, len); + buf[len] = '\0'; + + char *p = buf; + char *pend = p; + + errno = 0; + *pv = f (p, &pend); + if (unlikely (errno || p == pend || + /* Check if consumed whole buffer if is requested */ + (whole_buffer && pend - p != end - *pp))) + return false; + + *pp += pend - p; + return true; +} + +bool +hb_parse_int (const char **pp, const char *end, int *pv, bool whole_buffer) +{ + return _parse_number (pp, end, pv, whole_buffer, + [] (const char *p, char **end) + { return strtol (p, end, 10); }); +} + +bool +hb_parse_uint (const char **pp, const char *end, unsigned *pv, + bool whole_buffer, int base) +{ + return _parse_number (pp, end, pv, whole_buffer, + [base] (const char *p, char **end) + { return strtoul (p, end, base); }); +} + +bool +hb_parse_double (const char **pp, const char *end, double *pv, bool whole_buffer) +{ + const char *pend = end; + *pv = strtod_rl (*pp, &pend); + if (unlikely (*pp == pend)) return false; + *pp = pend; + return !whole_buffer || end == pend; +} diff --git a/src/3rdparty/harfbuzz-ng/src/hb-number.hh b/src/3rdparty/harfbuzz-ng/src/hb-number.hh new file mode 100644 index 00000000000..14d1260aa3d --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-number.hh @@ -0,0 +1,41 @@ +/* + * Copyright © 2019 Ebrahim Byagowi + * + * 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. + * + */ + +#ifndef HB_NUMBER_HH +#define HB_NUMBER_HH + +HB_INTERNAL bool +hb_parse_int (const char **pp, const char *end, int *pv, + bool whole_buffer = false); + +HB_INTERNAL bool +hb_parse_uint (const char **pp, const char *end, unsigned int *pv, + bool whole_buffer = false, int base = 10); + +HB_INTERNAL bool +hb_parse_double (const char **pp, const char *end, double *pv, + bool whole_buffer = false); + +#endif /* HB_NUMBER_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-object-private.hh b/src/3rdparty/harfbuzz-ng/src/hb-object-private.hh deleted file mode 100644 index baa1f8f05cc..00000000000 --- a/src/3rdparty/harfbuzz-ng/src/hb-object-private.hh +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright © 2007 Chris Wilson - * Copyright © 2009,2010 Red Hat, Inc. - * Copyright © 2011,2012 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. - * - * Contributor(s): - * Chris Wilson - * Red Hat Author(s): Behdad Esfahbod - * Google Author(s): Behdad Esfahbod - */ - -#ifndef HB_OBJECT_PRIVATE_HH -#define HB_OBJECT_PRIVATE_HH - -#include "hb-private.hh" -#include "hb-debug.hh" - -#include "hb-atomic-private.hh" -#include "hb-mutex-private.hh" - - -/* reference_count */ - -#define HB_REFERENCE_COUNT_INERT_VALUE -1 -#define HB_REFERENCE_COUNT_POISON_VALUE -0x0000DEAD -#define HB_REFERENCE_COUNT_INIT {HB_ATOMIC_INT_INIT(HB_REFERENCE_COUNT_INERT_VALUE)} - -struct hb_reference_count_t -{ - hb_atomic_int_t ref_count; - - inline void init (int v) { ref_count.set_unsafe (v); } - inline int get_unsafe (void) const { return ref_count.get_unsafe (); } - inline int inc (void) { return ref_count.inc (); } - inline int dec (void) { return ref_count.dec (); } - inline void finish (void) { ref_count.set_unsafe (HB_REFERENCE_COUNT_POISON_VALUE); } - - inline bool is_inert (void) const { return ref_count.get_unsafe () == HB_REFERENCE_COUNT_INERT_VALUE; } - inline bool is_valid (void) const { return ref_count.get_unsafe () > 0; } -}; - - -/* user_data */ - -#define HB_USER_DATA_ARRAY_INIT {HB_MUTEX_INIT, HB_LOCKABLE_SET_INIT} -struct hb_user_data_array_t -{ - struct hb_user_data_item_t { - hb_user_data_key_t *key; - void *data; - hb_destroy_func_t destroy; - - inline bool operator == (hb_user_data_key_t *other_key) const { return key == other_key; } - inline bool operator == (hb_user_data_item_t &other) const { return key == other.key; } - - void finish (void) { if (destroy) destroy (data); } - }; - - hb_mutex_t lock; - hb_lockable_set_t items; - - inline void init (void) { lock.init (); items.init (); } - - HB_INTERNAL bool set (hb_user_data_key_t *key, - void * data, - hb_destroy_func_t destroy, - hb_bool_t replace); - - HB_INTERNAL void *get (hb_user_data_key_t *key); - - inline void finish (void) { items.finish (lock); lock.finish (); } -}; - - -/* object_header */ - -struct hb_object_header_t -{ - hb_reference_count_t ref_count; - hb_user_data_array_t user_data; - -#define HB_OBJECT_HEADER_STATIC {HB_REFERENCE_COUNT_INIT, HB_USER_DATA_ARRAY_INIT} - - private: - ASSERT_POD (); -}; - - -/* object */ - -template -static inline void hb_object_trace (const Type *obj, const char *function) -{ - DEBUG_MSG (OBJECT, (void *) obj, - "%s refcount=%d", - function, - obj ? obj->header.ref_count.get_unsafe () : 0); -} - -template -static inline Type *hb_object_create (void) -{ - Type *obj = (Type *) calloc (1, sizeof (Type)); - - if (unlikely (!obj)) - return obj; - - hb_object_init (obj); - hb_object_trace (obj, HB_FUNC); - return obj; -} -template -static inline void hb_object_init (Type *obj) -{ - obj->header.ref_count.init (1); - obj->header.user_data.init (); -} -template -static inline bool hb_object_is_inert (const Type *obj) -{ - return unlikely (obj->header.ref_count.is_inert ()); -} -template -static inline bool hb_object_is_valid (const Type *obj) -{ - return likely (obj->header.ref_count.is_valid ()); -} -template -static inline Type *hb_object_reference (Type *obj) -{ - hb_object_trace (obj, HB_FUNC); - if (unlikely (!obj || hb_object_is_inert (obj))) - return obj; - assert (hb_object_is_valid (obj)); - obj->header.ref_count.inc (); - return obj; -} -template -static inline bool hb_object_destroy (Type *obj) -{ - hb_object_trace (obj, HB_FUNC); - if (unlikely (!obj || hb_object_is_inert (obj))) - return false; - assert (hb_object_is_valid (obj)); - if (obj->header.ref_count.dec () != 1) - return false; - - obj->header.ref_count.finish (); /* Do this before user_data */ - obj->header.user_data.finish (); - return true; -} -template -static inline bool hb_object_set_user_data (Type *obj, - hb_user_data_key_t *key, - void * data, - hb_destroy_func_t destroy, - hb_bool_t replace) -{ - if (unlikely (!obj || hb_object_is_inert (obj))) - return false; - assert (hb_object_is_valid (obj)); - return obj->header.user_data.set (key, data, destroy, replace); -} - -template -static inline void *hb_object_get_user_data (Type *obj, - hb_user_data_key_t *key) -{ - if (unlikely (!obj || hb_object_is_inert (obj))) - return nullptr; - assert (hb_object_is_valid (obj)); - return obj->header.user_data.get (key); -} - - -#endif /* HB_OBJECT_PRIVATE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-object.hh b/src/3rdparty/harfbuzz-ng/src/hb-object.hh new file mode 100644 index 00000000000..e2c2c3394cb --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-object.hh @@ -0,0 +1,357 @@ +/* + * Copyright © 2007 Chris Wilson + * Copyright © 2009,2010 Red Hat, Inc. + * Copyright © 2011,2012 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. + * + * Contributor(s): + * Chris Wilson + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OBJECT_HH +#define HB_OBJECT_HH + +#include "hb.hh" +#include "hb-atomic.hh" +#include "hb-mutex.hh" +#include "hb-vector.hh" + + +/* + * Lockable set + */ + +template +struct hb_lockable_set_t +{ + hb_vector_t items; + + void init () { items.init (); } + + template + item_t *replace_or_insert (T v, lock_t &l, bool replace) + { + l.lock (); + item_t *item = items.lsearch (v); + if (item) { + if (replace) { + item_t old = *item; + *item = v; + l.unlock (); + old.fini (); + } + else { + item = nullptr; + l.unlock (); + } + } else { + item = items.push (v); + l.unlock (); + } + return items.in_error () ? nullptr : item; + } + + template + void remove (T v, lock_t &l) + { + l.lock (); + item_t *item = items.lsearch (v); + if (item) + { + item_t old = *item; + *item = std::move (items.tail ()); + items.pop (); + l.unlock (); + old.fini (); + } else { + l.unlock (); + } + } + + template + bool find (T v, item_t *i, lock_t &l) + { + l.lock (); + item_t *item = items.lsearch (v); + if (item) + *i = *item; + l.unlock (); + return !!item; + } + + template + item_t *find_or_insert (T v, lock_t &l) + { + l.lock (); + item_t *item = items.find (v); + if (!item) { + item = items.push (v); + } + l.unlock (); + return item; + } + + void fini (lock_t &l) + { + if (!items.length) + { + /* No need to lock. */ + items.fini (); + return; + } + l.lock (); + while (items.length) + { + item_t old = items.tail (); + items.pop (); + l.unlock (); + old.fini (); + l.lock (); + } + items.fini (); + l.unlock (); + } + +}; + + +/* + * Reference-count. + */ + +struct hb_reference_count_t +{ + mutable hb_atomic_int_t ref_count; + + void init (int v = 1) { ref_count = v; } + int get_relaxed () const { return ref_count; } + int inc () const { return ref_count.inc (); } + int dec () const { return ref_count.dec (); } + void fini () { ref_count = -0x0000DEAD; } + + bool is_inert () const { return !ref_count; } + bool is_valid () const { return ref_count > 0; } +}; + + +/* user_data */ + +struct hb_user_data_array_t +{ + struct hb_user_data_item_t { + hb_user_data_key_t *key; + void *data; + hb_destroy_func_t destroy; + + bool operator == (const hb_user_data_key_t *other_key) const { return key == other_key; } + bool operator == (const hb_user_data_item_t &other) const { return key == other.key; } + + void fini () { if (destroy) destroy (data); } + }; + + hb_mutex_t lock; + hb_lockable_set_t items; + + void init () { lock.init (); items.init (); } + + void fini () { items.fini (lock); lock.fini (); } + + bool set (hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) + { + if (!key) + return false; + + if (replace) { + if (!data && !destroy) { + items.remove (key, lock); + return true; + } + } + hb_user_data_item_t item = {key, data, destroy}; + bool ret = !!items.replace_or_insert (item, lock, (bool) replace); + + return ret; + } + + void *get (hb_user_data_key_t *key) + { + hb_user_data_item_t item = {nullptr, nullptr, nullptr}; + + return items.find (key, &item, lock) ? item.data : nullptr; + } +}; + + +/* + * Object header + */ + +struct hb_object_header_t +{ + hb_reference_count_t ref_count; + mutable hb_atomic_int_t writable = 0; + hb_atomic_ptr_t user_data; + + bool is_inert () const { return !ref_count.get_relaxed (); } +}; +#define HB_OBJECT_HEADER_STATIC {} + + +/* + * Object + */ + +template +static inline void hb_object_trace (const Type *obj, const char *function) +{ + DEBUG_MSG (OBJECT, (void *) obj, + "%s refcount=%d", + function, + obj ? obj->header.ref_count.get_relaxed () : 0); +} + +template +static inline Type *hb_object_create (Ts... ds) +{ + Type *obj = (Type *) hb_calloc (1, sizeof (Type)); + + if (unlikely (!obj)) + return obj; + + new (obj) Type (std::forward (ds)...); + + hb_object_init (obj); + hb_object_trace (obj, HB_FUNC); + + return obj; +} +template +static inline void hb_object_init (Type *obj) +{ + obj->header.ref_count.init (); + obj->header.writable = true; + obj->header.user_data.init (); +} +template +static inline bool hb_object_is_valid (const Type *obj) +{ + return likely (obj->header.ref_count.is_valid ()); +} +template +static inline bool hb_object_is_immutable (const Type *obj) +{ + return !obj->header.writable; +} +template +static inline void hb_object_make_immutable (const Type *obj) +{ + obj->header.writable = false; +} +template +static inline Type *hb_object_reference (Type *obj) +{ + hb_object_trace (obj, HB_FUNC); + if (unlikely (!obj || obj->header.is_inert ())) + return obj; + assert (hb_object_is_valid (obj)); + obj->header.ref_count.inc (); + return obj; +} +template +static inline bool hb_object_destroy (Type *obj) +{ + hb_object_trace (obj, HB_FUNC); + if (unlikely (!obj || obj->header.is_inert ())) + return false; + assert (hb_object_is_valid (obj)); + if (obj->header.ref_count.dec () != 1) + return false; + + hb_object_fini (obj); + + if (!std::is_trivially_destructible::value) + obj->~Type (); + + return true; +} +template +static inline void hb_object_fini (Type *obj) +{ + obj->header.ref_count.fini (); /* Do this before user_data */ + hb_user_data_array_t *user_data = obj->header.user_data.get_acquire (); + if (user_data) + { + user_data->fini (); + hb_free (user_data); + obj->header.user_data.set_relaxed (nullptr); + } +} +template +static inline bool hb_object_set_user_data (Type *obj, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + if (unlikely (!obj || obj->header.is_inert ())) + return false; + assert (hb_object_is_valid (obj)); + +retry: + hb_user_data_array_t *user_data = obj->header.user_data.get_acquire (); + if (unlikely (!user_data)) + { + user_data = (hb_user_data_array_t *) hb_calloc (sizeof (hb_user_data_array_t), 1); + if (unlikely (!user_data)) + return false; + user_data->init (); + if (unlikely (!obj->header.user_data.cmpexch (nullptr, user_data))) + { + user_data->fini (); + hb_free (user_data); + goto retry; + } + } + + return user_data->set (key, data, destroy, replace); +} + +template +static inline void *hb_object_get_user_data (Type *obj, + hb_user_data_key_t *key) +{ + if (unlikely (!obj || obj->header.is_inert ())) + return nullptr; + assert (hb_object_is_valid (obj)); + hb_user_data_array_t *user_data = obj->header.user_data.get_acquire (); + if (!user_data) + return nullptr; + return user_data->get (key); +} + + +#endif /* HB_OBJECT_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-open-file-private.hh b/src/3rdparty/harfbuzz-ng/src/hb-open-file-private.hh deleted file mode 100644 index 644e0b40f6f..00000000000 --- a/src/3rdparty/harfbuzz-ng/src/hb-open-file-private.hh +++ /dev/null @@ -1,280 +0,0 @@ -/* - * Copyright © 2007,2008,2009 Red Hat, Inc. - * Copyright © 2012 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. - * - * Red Hat Author(s): Behdad Esfahbod - * Google Author(s): Behdad Esfahbod - */ - -#ifndef HB_OPEN_FILE_PRIVATE_HH -#define HB_OPEN_FILE_PRIVATE_HH - -#include "hb-open-type-private.hh" - - -namespace OT { - - -/* - * - * The OpenType Font File - * - */ - - -/* - * Organization of an OpenType Font - */ - -struct OpenTypeFontFile; -struct OffsetTable; -struct TTCHeader; - - -typedef struct TableRecord -{ - int cmp (Tag t) const - { return t.cmp (tag); } - - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (c->check_struct (this)); - } - - Tag tag; /* 4-byte identifier. */ - CheckSum checkSum; /* CheckSum for this table. */ - UINT32 offset; /* Offset from beginning of TrueType font - * file. */ - UINT32 length; /* Length of this table. */ - public: - DEFINE_SIZE_STATIC (16); -} OpenTypeTable; - -typedef struct OffsetTable -{ - friend struct OpenTypeFontFile; - - inline unsigned int get_table_count (void) const - { return tables.len; } - inline const TableRecord& get_table (unsigned int i) const - { - return tables[i]; - } - inline unsigned int get_table_tags (unsigned int start_offset, - unsigned int *table_count, /* IN/OUT */ - hb_tag_t *table_tags /* OUT */) const - { - if (table_count) - { - if (start_offset >= tables.len) - *table_count = 0; - else - *table_count = MIN (*table_count, tables.len - start_offset); - - const TableRecord *sub_tables = tables.array + start_offset; - unsigned int count = *table_count; - for (unsigned int i = 0; i < count; i++) - table_tags[i] = sub_tables[i].tag; - } - return tables.len; - } - inline bool find_table_index (hb_tag_t tag, unsigned int *table_index) const - { - Tag t; - t.set (tag); - /* Linear-search for small tables to work around fonts with unsorted - * table list. */ - int i = tables.len < 64 ? tables.lsearch (t) : tables.bsearch (t); - if (table_index) - *table_index = i == -1 ? Index::NOT_FOUND_INDEX : (unsigned int) i; - return i != -1; - } - inline const TableRecord& get_table_by_tag (hb_tag_t tag) const - { - unsigned int table_index; - find_table_index (tag, &table_index); - return get_table (table_index); - } - - public: - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (c->check_struct (this) && tables.sanitize (c)); - } - - protected: - Tag sfnt_version; /* '\0\001\0\00' if TrueType / 'OTTO' if CFF */ - BinSearchArrayOf - tables; - public: - DEFINE_SIZE_ARRAY (12, tables); -} OpenTypeFontFace; - - -/* - * TrueType Collections - */ - -struct TTCHeaderVersion1 -{ - friend struct TTCHeader; - - inline unsigned int get_face_count (void) const { return table.len; } - inline const OpenTypeFontFace& get_face (unsigned int i) const { return this+table[i]; } - - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (table.sanitize (c, this)); - } - - protected: - Tag ttcTag; /* TrueType Collection ID string: 'ttcf' */ - FixedVersion<>version; /* Version of the TTC Header (1.0), - * 0x00010000u */ - ArrayOf, UINT32> - table; /* Array of offsets to the OffsetTable for each font - * from the beginning of the file */ - public: - DEFINE_SIZE_ARRAY (12, table); -}; - -struct TTCHeader -{ - friend struct OpenTypeFontFile; - - private: - - inline unsigned int get_face_count (void) const - { - switch (u.header.version.major) { - case 2: /* version 2 is compatible with version 1 */ - case 1: return u.version1.get_face_count (); - default:return 0; - } - } - inline const OpenTypeFontFace& get_face (unsigned int i) const - { - switch (u.header.version.major) { - case 2: /* version 2 is compatible with version 1 */ - case 1: return u.version1.get_face (i); - default:return Null(OpenTypeFontFace); - } - } - - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - if (unlikely (!u.header.version.sanitize (c))) return_trace (false); - switch (u.header.version.major) { - case 2: /* version 2 is compatible with version 1 */ - case 1: return_trace (u.version1.sanitize (c)); - default:return_trace (true); - } - } - - protected: - union { - struct { - Tag ttcTag; /* TrueType Collection ID string: 'ttcf' */ - FixedVersion<>version; /* Version of the TTC Header (1.0 or 2.0), - * 0x00010000u or 0x00020000u */ - } header; - TTCHeaderVersion1 version1; - } u; -}; - - -/* - * OpenType Font File - */ - -struct OpenTypeFontFile -{ - static const hb_tag_t tableTag = HB_TAG ('_','_','_','_'); /* Sanitizer needs this. */ - - static const hb_tag_t CFFTag = HB_TAG ('O','T','T','O'); /* OpenType with Postscript outlines */ - static const hb_tag_t TrueTypeTag = HB_TAG ( 0 , 1 , 0 , 0 ); /* OpenType with TrueType outlines */ - static const hb_tag_t TTCTag = HB_TAG ('t','t','c','f'); /* TrueType Collection */ - static const hb_tag_t TrueTag = HB_TAG ('t','r','u','e'); /* Obsolete Apple TrueType */ - static const hb_tag_t Typ1Tag = HB_TAG ('t','y','p','1'); /* Obsolete Apple Type1 font in SFNT container */ - - inline hb_tag_t get_tag (void) const { return u.tag; } - - inline unsigned int get_face_count (void) const - { - switch (u.tag) { - case CFFTag: /* All the non-collection tags */ - case TrueTag: - case Typ1Tag: - case TrueTypeTag: return 1; - case TTCTag: return u.ttcHeader.get_face_count (); - default: return 0; - } - } - inline const OpenTypeFontFace& get_face (unsigned int i) const - { - switch (u.tag) { - /* Note: for non-collection SFNT data we ignore index. This is because - * Apple dfont container is a container of SFNT's. So each SFNT is a - * non-TTC, but the index is more than zero. */ - case CFFTag: /* All the non-collection tags */ - case TrueTag: - case Typ1Tag: - case TrueTypeTag: return u.fontFace; - case TTCTag: return u.ttcHeader.get_face (i); - default: return Null(OpenTypeFontFace); - } - } - - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - if (unlikely (!u.tag.sanitize (c))) return_trace (false); - switch (u.tag) { - case CFFTag: /* All the non-collection tags */ - case TrueTag: - case Typ1Tag: - case TrueTypeTag: return_trace (u.fontFace.sanitize (c)); - case TTCTag: return_trace (u.ttcHeader.sanitize (c)); - default: return_trace (true); - } - } - - protected: - union { - Tag tag; /* 4-byte identifier. */ - OpenTypeFontFace fontFace; - TTCHeader ttcHeader; - } u; - public: - DEFINE_SIZE_UNION (4, tag); -}; - - -} /* namespace OT */ - - -#endif /* HB_OPEN_FILE_PRIVATE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-open-file.hh b/src/3rdparty/harfbuzz-ng/src/hb-open-file.hh new file mode 100644 index 00000000000..04f144a72c7 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-open-file.hh @@ -0,0 +1,537 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2012 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OPEN_FILE_HH +#define HB_OPEN_FILE_HH + +#include "hb-open-type.hh" +#include "hb-ot-head-table.hh" + + +namespace OT { + +/* + * + * The OpenType Font File + * + */ + + +/* + * Organization of an OpenType Font + */ + +struct OpenTypeFontFile; +struct OpenTypeOffsetTable; +struct TTCHeader; + + +typedef struct TableRecord +{ + int cmp (Tag t) const { return -t.cmp (tag); } + + HB_INTERNAL static int cmp (const void *pa, const void *pb) + { + const TableRecord *a = (const TableRecord *) pa; + const TableRecord *b = (const TableRecord *) pb; + return b->cmp (a->tag); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + Tag tag; /* 4-byte identifier. */ + CheckSum checkSum; /* CheckSum for this table. */ + Offset32 offset; /* Offset from beginning of TrueType font + * file. */ + HBUINT32 length; /* Length of this table. */ + public: + DEFINE_SIZE_STATIC (16); +} OpenTypeTable; + +typedef struct OpenTypeOffsetTable +{ + friend struct OpenTypeFontFile; + + unsigned int get_table_count () const { return tables.len; } + const TableRecord& get_table (unsigned int i) const + { return tables[i]; } + unsigned int get_table_tags (unsigned int start_offset, + unsigned int *table_count, /* IN/OUT */ + hb_tag_t *table_tags /* OUT */) const + { + if (table_count) + { + + tables.as_array ().sub_array (start_offset, table_count) + | hb_map (&TableRecord::tag) + | hb_sink (hb_array (table_tags, *table_count)) + ; + } + return tables.len; + } + bool find_table_index (hb_tag_t tag, unsigned int *table_index) const + { + Tag t; + t = tag; + /* Use lfind for small fonts; there are fonts that have unsorted table entries; + * those tend to work in other tools, so tolerate them. + * https://github.com/harfbuzz/harfbuzz/issues/3065 */ + if (tables.len < 16) + return tables.lfind (t, table_index, HB_NOT_FOUND_STORE, Index::NOT_FOUND_INDEX); + else + return tables.bfind (t, table_index, HB_NOT_FOUND_STORE, Index::NOT_FOUND_INDEX); + } + const TableRecord& get_table_by_tag (hb_tag_t tag) const + { + unsigned int table_index; + find_table_index (tag, &table_index); + return get_table (table_index); + } + + public: + + template >::value))> + bool serialize (hb_serialize_context_t *c, + hb_tag_t sfnt_tag, + Iterator it) + { + TRACE_SERIALIZE (this); + /* Alloc 12 for the OTHeader. */ + if (unlikely (!c->extend_min (this))) return_trace (false); + /* Write sfntVersion (bytes 0..3). */ + sfnt_version = sfnt_tag; + /* Take space for numTables, searchRange, entrySelector, RangeShift + * and the TableRecords themselves. */ + unsigned num_items = hb_len (it); + if (unlikely (!tables.serialize (c, num_items))) return_trace (false); + + const char *dir_end = (const char *) c->head; + HBUINT32 *checksum_adjustment = nullptr; + + /* Write OffsetTables, alloc for and write actual table blobs. */ + unsigned i = 0; + for (hb_pair_t entry : it) + { + hb_blob_t *blob = entry.second; + unsigned len = blob->length; + + /* Allocate room for the table and copy it. */ + char *start = (char *) c->allocate_size (len, false); + if (unlikely (!start)) return false; + + TableRecord &rec = tables.arrayZ[i]; + rec.tag = entry.first; + rec.length = len; + rec.offset = 0; + if (unlikely (!c->check_assign (rec.offset, + (unsigned) ((char *) start - (char *) this), + HB_SERIALIZE_ERROR_OFFSET_OVERFLOW))) + return_trace (false); + + if (likely (len)) + hb_memcpy (start, blob->data, len); + + /* 4-byte alignment. */ + c->align (4); + const char *end = (const char *) c->head; + + if (entry.first == HB_OT_TAG_head && + (unsigned) (end - start) >= head::static_size) + { + head *h = (head *) start; + checksum_adjustment = &h->checkSumAdjustment; + *checksum_adjustment = 0; + } + + rec.checkSum.set_for_data (start, end - start); + i++; + } + + tables.qsort (); + + if (checksum_adjustment) + { + CheckSum checksum; + + /* The following line is a slower version of the following block. */ + //checksum.set_for_data (this, (const char *) c->head - (const char *) this); + checksum.set_for_data (this, dir_end - (const char *) this); + for (unsigned int i = 0; i < num_items; i++) + { + TableRecord &rec = tables.arrayZ[i]; + checksum = checksum + rec.checkSum; + } + + *checksum_adjustment = 0xB1B0AFBAu - checksum; + } + + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && tables.sanitize (c)); + } + + protected: + Tag sfnt_version; /* '\0\001\0\00' if TrueType / 'OTTO' if CFF */ + BinSearchArrayOf + tables; + public: + DEFINE_SIZE_ARRAY (12, tables); +} OpenTypeFontFace; + + +/* + * TrueType Collections + */ + +struct TTCHeaderVersion1 +{ + friend struct TTCHeader; + + unsigned int get_face_count () const { return table.len; } + const OpenTypeFontFace& get_face (unsigned int i) const { return this+table[i]; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (table.sanitize (c, this)); + } + + protected: + Tag ttcTag; /* TrueType Collection ID string: 'ttcf' */ + FixedVersion<>version; /* Version of the TTC Header (1.0), + * 0x00010000u */ + Array32Of> + table; /* Array of offsets to the OffsetTable for each font + * from the beginning of the file */ + public: + DEFINE_SIZE_ARRAY (12, table); +}; + +struct TTCHeader +{ + friend struct OpenTypeFontFile; + + private: + + unsigned int get_face_count () const + { + switch (u.header.version.major) { + case 2: /* version 2 is compatible with version 1 */ + case 1: return u.version1.get_face_count (); + default:return 0; + } + } + const OpenTypeFontFace& get_face (unsigned int i) const + { + switch (u.header.version.major) { + case 2: /* version 2 is compatible with version 1 */ + case 1: return u.version1.get_face (i); + default:return Null (OpenTypeFontFace); + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!u.header.version.sanitize (c))) return_trace (false); + switch (u.header.version.major) { + case 2: /* version 2 is compatible with version 1 */ + case 1: return_trace (u.version1.sanitize (c)); + default:return_trace (true); + } + } + + protected: + union { + struct { + Tag ttcTag; /* TrueType Collection ID string: 'ttcf' */ + FixedVersion<>version; /* Version of the TTC Header (1.0 or 2.0), + * 0x00010000u or 0x00020000u */ + } header; + TTCHeaderVersion1 version1; + } u; +}; + +/* + * Mac Resource Fork + * + * http://mirror.informatimago.com/next/developer.apple.com/documentation/mac/MoreToolbox/MoreToolbox-99.html + */ + +struct ResourceRecord +{ + const OpenTypeFontFace & get_face (const void *data_base) const + { return * reinterpret_cast ((data_base+offset).arrayZ); } + + bool sanitize (hb_sanitize_context_t *c, + const void *data_base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + offset.sanitize (c, data_base) && + get_face (data_base).sanitize (c)); + } + + protected: + HBUINT16 id; /* Resource ID. */ + HBINT16 nameOffset; /* Offset from beginning of resource name list + * to resource name, -1 means there is none. */ + HBUINT8 attrs; /* Resource attributes */ + NNOffset24To> + offset; /* Offset from beginning of data block to + * data for this resource */ + HBUINT32 reserved; /* Reserved for handle to resource */ + public: + DEFINE_SIZE_STATIC (12); +}; + +#define HB_TAG_sfnt HB_TAG ('s','f','n','t') + +struct ResourceTypeRecord +{ + unsigned int get_resource_count () const + { return tag == HB_TAG_sfnt ? resCountM1 + 1 : 0; } + + bool is_sfnt () const { return tag == HB_TAG_sfnt; } + + const ResourceRecord& get_resource_record (unsigned int i, + const void *type_base) const + { return (type_base+resourcesZ).as_array (get_resource_count ())[i]; } + + bool sanitize (hb_sanitize_context_t *c, + const void *type_base, + const void *data_base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + resourcesZ.sanitize (c, type_base, + get_resource_count (), + data_base)); + } + + protected: + Tag tag; /* Resource type. */ + HBUINT16 resCountM1; /* Number of resources minus 1. */ + NNOffset16To> + resourcesZ; /* Offset from beginning of resource type list + * to reference item list for this type. */ + public: + DEFINE_SIZE_STATIC (8); +}; + +struct ResourceMap +{ + unsigned int get_face_count () const + { + unsigned int count = get_type_count (); + for (unsigned int i = 0; i < count; i++) + { + const ResourceTypeRecord& type = get_type_record (i); + if (type.is_sfnt ()) + return type.get_resource_count (); + } + return 0; + } + + const OpenTypeFontFace& get_face (unsigned int idx, + const void *data_base) const + { + unsigned int count = get_type_count (); + for (unsigned int i = 0; i < count; i++) + { + const ResourceTypeRecord& type = get_type_record (i); + /* The check for idx < count is here because ResourceRecord is NOT null-safe. + * Because an offset of 0 there does NOT mean null. */ + if (type.is_sfnt () && idx < type.get_resource_count ()) + return type.get_resource_record (idx, &(this+typeList)).get_face (data_base); + } + return Null (OpenTypeFontFace); + } + + bool sanitize (hb_sanitize_context_t *c, const void *data_base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + typeList.sanitize (c, this, + &(this+typeList), + data_base)); + } + + private: + unsigned int get_type_count () const { return (this+typeList).lenM1 + 1; } + + const ResourceTypeRecord& get_type_record (unsigned int i) const + { return (this+typeList)[i]; } + + protected: + HBUINT8 reserved0[16]; /* Reserved for copy of resource header */ + HBUINT32 reserved1; /* Reserved for handle to next resource map */ + HBUINT16 resreved2; /* Reserved for file reference number */ + HBUINT16 attrs; /* Resource fork attribute */ + NNOffset16To> + typeList; /* Offset from beginning of map to + * resource type list */ + Offset16 nameList; /* Offset from beginning of map to + * resource name list */ + public: + DEFINE_SIZE_STATIC (28); +}; + +struct ResourceForkHeader +{ + unsigned int get_face_count () const + { return (this+map).get_face_count (); } + + const OpenTypeFontFace& get_face (unsigned int idx, + unsigned int *base_offset = nullptr) const + { + const OpenTypeFontFace &face = (this+map).get_face (idx, &(this+data)); + if (base_offset) + *base_offset = (const char *) &face - (const char *) this; + return face; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + data.sanitize (c, this, dataLen) && + map.sanitize (c, this, &(this+data))); + } + + protected: + NNOffset32To> + data; /* Offset from beginning of resource fork + * to resource data */ + NNOffset32To + map; /* Offset from beginning of resource fork + * to resource map */ + HBUINT32 dataLen; /* Length of resource data */ + HBUINT32 mapLen; /* Length of resource map */ + public: + DEFINE_SIZE_STATIC (16); +}; + +/* + * OpenType Font File + */ + +struct OpenTypeFontFile +{ + enum { + CFFTag = HB_TAG ('O','T','T','O'), /* OpenType with Postscript outlines */ + TrueTypeTag = HB_TAG ( 0 , 1 , 0 , 0 ), /* OpenType with TrueType outlines */ + TTCTag = HB_TAG ('t','t','c','f'), /* TrueType Collection */ + DFontTag = HB_TAG ( 0 , 0 , 1 , 0 ), /* DFont Mac Resource Fork */ + TrueTag = HB_TAG ('t','r','u','e'), /* Obsolete Apple TrueType */ + Typ1Tag = HB_TAG ('t','y','p','1') /* Obsolete Apple Type1 font in SFNT container */ + }; + + hb_tag_t get_tag () const { return u.tag; } + + unsigned int get_face_count () const + { + switch (u.tag) { + case CFFTag: /* All the non-collection tags */ + case TrueTag: + case Typ1Tag: + case TrueTypeTag: return 1; + case TTCTag: return u.ttcHeader.get_face_count (); + case DFontTag: return u.rfHeader.get_face_count (); + default: return 0; + } + } + const OpenTypeFontFace& get_face (unsigned int i, unsigned int *base_offset = nullptr) const + { + if (base_offset) + *base_offset = 0; + switch (u.tag) { + /* Note: for non-collection SFNT data we ignore index. This is because + * Apple dfont container is a container of SFNT's. So each SFNT is a + * non-TTC, but the index is more than zero. */ + case CFFTag: /* All the non-collection tags */ + case TrueTag: + case Typ1Tag: + case TrueTypeTag: return u.fontFace; + case TTCTag: return u.ttcHeader.get_face (i); + case DFontTag: return u.rfHeader.get_face (i, base_offset); + default: return Null (OpenTypeFontFace); + } + } + + template >::value))> + bool serialize_single (hb_serialize_context_t *c, + hb_tag_t sfnt_tag, + Iterator items) + { + TRACE_SERIALIZE (this); + assert (sfnt_tag != TTCTag); + if (unlikely (!c->extend_min (this))) return_trace (false); + return_trace (u.fontFace.serialize (c, sfnt_tag, items)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!u.tag.sanitize (c))) return_trace (false); + switch (u.tag) { + case CFFTag: /* All the non-collection tags */ + case TrueTag: + case Typ1Tag: + case TrueTypeTag: return_trace (u.fontFace.sanitize (c)); + case TTCTag: return_trace (u.ttcHeader.sanitize (c)); + case DFontTag: return_trace (u.rfHeader.sanitize (c)); + default: return_trace (true); + } + } + + protected: + union { + Tag tag; /* 4-byte identifier. */ + OpenTypeFontFace fontFace; + TTCHeader ttcHeader; + ResourceForkHeader rfHeader; + } u; + public: + DEFINE_SIZE_UNION (4, tag); +}; + + +} /* namespace OT */ + + +#endif /* HB_OPEN_FILE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-open-type-private.hh b/src/3rdparty/harfbuzz-ng/src/hb-open-type-private.hh deleted file mode 100644 index 2f4e1b9e9e9..00000000000 --- a/src/3rdparty/harfbuzz-ng/src/hb-open-type-private.hh +++ /dev/null @@ -1,1184 +0,0 @@ -/* - * Copyright © 2007,2008,2009,2010 Red Hat, Inc. - * Copyright © 2012 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. - * - * Red Hat Author(s): Behdad Esfahbod - * Google Author(s): Behdad Esfahbod - */ - -#ifndef HB_OPEN_TYPE_PRIVATE_HH -#define HB_OPEN_TYPE_PRIVATE_HH - -#include "hb-private.hh" -#include "hb-debug.hh" -#include "hb-face-private.hh" - - -namespace OT { - - - -/* - * Casts - */ - -/* Cast to struct T, reference to reference */ -template -static inline const Type& CastR(const TObject &X) -{ return reinterpret_cast (X); } -template -static inline Type& CastR(TObject &X) -{ return reinterpret_cast (X); } - -/* Cast to struct T, pointer to pointer */ -template -static inline const Type* CastP(const TObject *X) -{ return reinterpret_cast (X); } -template -static inline Type* CastP(TObject *X) -{ return reinterpret_cast (X); } - -/* StructAtOffset(P,Ofs) returns the struct T& that is placed at memory - * location pointed to by P plus Ofs bytes. */ -template -static inline const Type& StructAtOffset(const void *P, unsigned int offset) -{ return * reinterpret_cast ((const char *) P + offset); } -template -static inline Type& StructAtOffset(void *P, unsigned int offset) -{ return * reinterpret_cast ((char *) P + offset); } - -/* StructAfter(X) returns the struct T& that is placed after X. - * Works with X of variable size also. X must implement get_size() */ -template -static inline const Type& StructAfter(const TObject &X) -{ return StructAtOffset(&X, X.get_size()); } -template -static inline Type& StructAfter(TObject &X) -{ return StructAtOffset(&X, X.get_size()); } - - - -/* - * Size checking - */ - -/* Check _assertion in a method environment */ -#define _DEFINE_INSTANCE_ASSERTION1(_line, _assertion) \ - inline void _instance_assertion_on_line_##_line (void) const \ - { \ - static_assert ((_assertion), ""); \ - ASSERT_INSTANCE_POD (*this); /* Make sure it's POD. */ \ - } -# define _DEFINE_INSTANCE_ASSERTION0(_line, _assertion) _DEFINE_INSTANCE_ASSERTION1 (_line, _assertion) -# define DEFINE_INSTANCE_ASSERTION(_assertion) _DEFINE_INSTANCE_ASSERTION0 (__LINE__, _assertion) - -/* Check that _code compiles in a method environment */ -#define _DEFINE_COMPILES_ASSERTION1(_line, _code) \ - inline void _compiles_assertion_on_line_##_line (void) const \ - { _code; } -# define _DEFINE_COMPILES_ASSERTION0(_line, _code) _DEFINE_COMPILES_ASSERTION1 (_line, _code) -# define DEFINE_COMPILES_ASSERTION(_code) _DEFINE_COMPILES_ASSERTION0 (__LINE__, _code) - - -#define DEFINE_SIZE_STATIC(size) \ - DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size)); \ - static const unsigned int static_size = (size); \ - static const unsigned int min_size = (size); \ - inline unsigned int get_size (void) const { return (size); } - -#define DEFINE_SIZE_UNION(size, _member) \ - DEFINE_INSTANCE_ASSERTION (0*sizeof(this->u._member.static_size) + sizeof(this->u._member) == (size)); \ - static const unsigned int min_size = (size) - -#define DEFINE_SIZE_MIN(size) \ - DEFINE_INSTANCE_ASSERTION (sizeof (*this) >= (size)); \ - static const unsigned int min_size = (size) - -#define DEFINE_SIZE_ARRAY(size, array) \ - DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + sizeof (array[0])); \ - DEFINE_COMPILES_ASSERTION ((void) array[0].static_size) \ - static const unsigned int min_size = (size) - -#define DEFINE_SIZE_ARRAY2(size, array1, array2) \ - DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + sizeof (this->array1[0]) + sizeof (this->array2[0])); \ - DEFINE_COMPILES_ASSERTION ((void) array1[0].static_size; (void) array2[0].static_size) \ - static const unsigned int min_size = (size) - - - -/* - * Null objects - */ - -/* Global nul-content Null pool. Enlarge as necessary. */ - -#define HB_NULL_POOL_SIZE 264 -static_assert (HB_NULL_POOL_SIZE % sizeof (void *) == 0, "Align HB_NULL_POOL_SIZE."); -extern HB_INTERNAL const void * const _hb_NullPool[HB_NULL_POOL_SIZE / sizeof (void *)]; - -/* Generic nul-content Null objects. */ -template -static inline const Type& Null (void) { - static_assert (sizeof (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE."); - return *CastP (_hb_NullPool); -} - -/* Specializaiton for arbitrary-content arbitrary-sized Null objects. */ -#define DEFINE_NULL_DATA(Type, data) \ -static const char _Null##Type[sizeof (Type) + 1] = data; /* +1 is for nul-termination in data */ \ -template <> \ -/*static*/ inline const Type& Null (void) { \ - return *CastP (_Null##Type); \ -} /* The following line really exists such that we end in a place needing semicolon */ \ -static_assert (Type::min_size + 1 <= sizeof (_Null##Type), "Null pool too small. Enlarge.") - -/* Accessor macro. */ -#define Null(Type) Null() - - -/* - * Dispatch - */ - -template -struct hb_dispatch_context_t -{ - static const unsigned int max_debug_depth = MaxDebugDepth; - typedef Return return_t; - template - inline bool may_dispatch (const T *obj, const F *format) { return true; } - static return_t no_dispatch_return_value (void) { return Context::default_return_value (); } -}; - - -/* - * Sanitize - */ - -/* This limits sanitizing time on really broken fonts. */ -#ifndef HB_SANITIZE_MAX_EDITS -#define HB_SANITIZE_MAX_EDITS 32 -#endif - -struct hb_sanitize_context_t : - hb_dispatch_context_t -{ - inline hb_sanitize_context_t (void) : - debug_depth (0), - start (nullptr), end (nullptr), - writable (false), edit_count (0), - blob (nullptr) {} - - inline const char *get_name (void) { return "SANITIZE"; } - template - inline bool may_dispatch (const T *obj, const F *format) - { return format->sanitize (this); } - template - inline return_t dispatch (const T &obj) { return obj.sanitize (this); } - static return_t default_return_value (void) { return true; } - static return_t no_dispatch_return_value (void) { return false; } - bool stop_sublookup_iteration (const return_t r) const { return !r; } - - inline void init (hb_blob_t *b) - { - this->blob = hb_blob_reference (b); - this->writable = false; - } - - inline void start_processing (void) - { - this->start = hb_blob_get_data (this->blob, nullptr); - this->end = this->start + hb_blob_get_length (this->blob); - assert (this->start <= this->end); /* Must not overflow. */ - this->edit_count = 0; - this->debug_depth = 0; - - DEBUG_MSG_LEVEL (SANITIZE, start, 0, +1, - "start [%p..%p] (%lu bytes)", - this->start, this->end, - (unsigned long) (this->end - this->start)); - } - - inline void end_processing (void) - { - DEBUG_MSG_LEVEL (SANITIZE, this->start, 0, -1, - "end [%p..%p] %u edit requests", - this->start, this->end, this->edit_count); - - hb_blob_destroy (this->blob); - this->blob = nullptr; - this->start = this->end = nullptr; - } - - inline bool check_range (const void *base, unsigned int len) const - { - const char *p = (const char *) base; - bool ok = this->start <= p && p <= this->end && (unsigned int) (this->end - p) >= len; - - DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0, - "check_range [%p..%p] (%d bytes) in [%p..%p] -> %s", - p, p + len, len, - this->start, this->end, - ok ? "OK" : "OUT-OF-RANGE"); - - return likely (ok); - } - - inline bool check_array (const void *base, unsigned int record_size, unsigned int len) const - { - const char *p = (const char *) base; - bool overflows = _hb_unsigned_int_mul_overflows (len, record_size); - unsigned int array_size = record_size * len; - bool ok = !overflows && this->check_range (base, array_size); - - DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0, - "check_array [%p..%p] (%d*%d=%d bytes) in [%p..%p] -> %s", - p, p + (record_size * len), record_size, len, (unsigned int) array_size, - this->start, this->end, - overflows ? "OVERFLOWS" : ok ? "OK" : "OUT-OF-RANGE"); - - return likely (ok); - } - - template - inline bool check_struct (const Type *obj) const - { - return likely (this->check_range (obj, obj->min_size)); - } - - inline bool may_edit (const void *base HB_UNUSED, unsigned int len HB_UNUSED) - { - if (this->edit_count >= HB_SANITIZE_MAX_EDITS) - return false; - - const char *p = (const char *) base; - this->edit_count++; - - DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0, - "may_edit(%u) [%p..%p] (%d bytes) in [%p..%p] -> %s", - this->edit_count, - p, p + len, len, - this->start, this->end, - this->writable ? "GRANTED" : "DENIED"); - - return this->writable; - } - - template - inline bool try_set (const Type *obj, const ValueType &v) { - if (this->may_edit (obj, obj->static_size)) { - const_cast (obj)->set (v); - return true; - } - return false; - } - - mutable unsigned int debug_depth; - const char *start, *end; - bool writable; - unsigned int edit_count; - hb_blob_t *blob; -}; - - - -/* Template to sanitize an object. */ -template -struct Sanitizer -{ - static hb_blob_t *sanitize (hb_blob_t *blob) { - hb_sanitize_context_t c[1]; - bool sane; - - /* TODO is_sane() stuff */ - - c->init (blob); - - retry: - DEBUG_MSG_FUNC (SANITIZE, c->start, "start"); - - c->start_processing (); - - if (unlikely (!c->start)) { - c->end_processing (); - return blob; - } - - Type *t = CastP (const_cast (c->start)); - - sane = t->sanitize (c); - if (sane) { - if (c->edit_count) { - DEBUG_MSG_FUNC (SANITIZE, c->start, "passed first round with %d edits; going for second round", c->edit_count); - - /* sanitize again to ensure no toe-stepping */ - c->edit_count = 0; - sane = t->sanitize (c); - if (c->edit_count) { - DEBUG_MSG_FUNC (SANITIZE, c->start, "requested %d edits in second round; FAILLING", c->edit_count); - sane = false; - } - } - } else { - unsigned int edit_count = c->edit_count; - if (edit_count && !c->writable) { - c->start = hb_blob_get_data_writable (blob, nullptr); - c->end = c->start + hb_blob_get_length (blob); - - if (c->start) { - c->writable = true; - /* ok, we made it writable by relocating. try again */ - DEBUG_MSG_FUNC (SANITIZE, c->start, "retry"); - goto retry; - } - } - } - - c->end_processing (); - - DEBUG_MSG_FUNC (SANITIZE, c->start, sane ? "PASSED" : "FAILED"); - if (sane) - return blob; - else { - hb_blob_destroy (blob); - return hb_blob_get_empty (); - } - } - - static const Type* lock_instance (hb_blob_t *blob) { - hb_blob_make_immutable (blob); - const char *base = hb_blob_get_data (blob, nullptr); - return unlikely (!base) ? &Null(Type) : CastP (base); - } -}; - - - -/* - * Serialize - */ - - -struct hb_serialize_context_t -{ - inline hb_serialize_context_t (void *start_, unsigned int size) - { - this->start = (char *) start_; - this->end = this->start + size; - - this->ran_out_of_room = false; - this->head = this->start; - this->debug_depth = 0; - } - - template - inline Type *start_serialize (void) - { - DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, +1, - "start [%p..%p] (%lu bytes)", - this->start, this->end, - (unsigned long) (this->end - this->start)); - - return start_embed (); - } - - inline void end_serialize (void) - { - DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, -1, - "end [%p..%p] serialized %d bytes; %s", - this->start, this->end, - (int) (this->head - this->start), - this->ran_out_of_room ? "RAN OUT OF ROOM" : "did not ran out of room"); - - } - - template - inline Type *copy (void) - { - assert (!this->ran_out_of_room); - unsigned int len = this->head - this->start; - void *p = malloc (len); - if (p) - memcpy (p, this->start, len); - return reinterpret_cast (p); - } - - template - inline Type *allocate_size (unsigned int size) - { - if (unlikely (this->ran_out_of_room || this->end - this->head < ptrdiff_t (size))) { - this->ran_out_of_room = true; - return nullptr; - } - memset (this->head, 0, size); - char *ret = this->head; - this->head += size; - return reinterpret_cast (ret); - } - - template - inline Type *allocate_min (void) - { - return this->allocate_size (Type::min_size); - } - - template - inline Type *start_embed (void) - { - Type *ret = reinterpret_cast (this->head); - return ret; - } - - template - inline Type *embed (const Type &obj) - { - unsigned int size = obj.get_size (); - Type *ret = this->allocate_size (size); - if (unlikely (!ret)) return nullptr; - memcpy (ret, obj, size); - return ret; - } - - template - inline Type *extend_min (Type &obj) - { - unsigned int size = obj.min_size; - assert (this->start <= (char *) &obj && (char *) &obj <= this->head && (char *) &obj + size >= this->head); - if (unlikely (!this->allocate_size (((char *) &obj) + size - this->head))) return nullptr; - return reinterpret_cast (&obj); - } - - template - inline Type *extend (Type &obj) - { - unsigned int size = obj.get_size (); - assert (this->start < (char *) &obj && (char *) &obj <= this->head && (char *) &obj + size >= this->head); - if (unlikely (!this->allocate_size (((char *) &obj) + size - this->head))) return nullptr; - return reinterpret_cast (&obj); - } - - inline void truncate (void *new_head) - { - assert (this->start < new_head && new_head <= this->head); - this->head = (char *) new_head; - } - - unsigned int debug_depth; - char *start, *end, *head; - bool ran_out_of_room; -}; - -template -struct Supplier -{ - inline Supplier (const Type *array, unsigned int len_) - { - head = array; - len = len_; - } - inline const Type operator [] (unsigned int i) const - { - if (unlikely (i >= len)) return Type (); - return head[i]; - } - - inline void advance (unsigned int count) - { - if (unlikely (count > len)) - count = len; - len -= count; - head += count; - } - - private: - inline Supplier (const Supplier &); /* Disallow copy */ - inline Supplier& operator= (const Supplier &); /* Disallow copy */ - - unsigned int len; - const Type *head; -}; - - - - -/* - * - * The OpenType Font File: Data Types - */ - - -/* "The following data types are used in the OpenType font file. - * All OpenType fonts use Motorola-style byte ordering (Big Endian):" */ - -/* - * Int types - */ - - -template struct BEInt; - -template -struct BEInt -{ - public: - inline void set (Type V) - { - v = V; - } - inline operator Type (void) const - { - return v; - } - private: uint8_t v; -}; -template -struct BEInt -{ - public: - inline void set (Type V) - { - v[0] = (V >> 8) & 0xFF; - v[1] = (V ) & 0xFF; - } - inline operator Type (void) const - { - return (v[0] << 8) - + (v[1] ); - } - private: uint8_t v[2]; -}; -template -struct BEInt -{ - public: - inline void set (Type V) - { - v[0] = (V >> 16) & 0xFF; - v[1] = (V >> 8) & 0xFF; - v[2] = (V ) & 0xFF; - } - inline operator Type (void) const - { - return (v[0] << 16) - + (v[1] << 8) - + (v[2] ); - } - private: uint8_t v[3]; -}; -template -struct BEInt -{ - public: - inline void set (Type V) - { - v[0] = (V >> 24) & 0xFF; - v[1] = (V >> 16) & 0xFF; - v[2] = (V >> 8) & 0xFF; - v[3] = (V ) & 0xFF; - } - inline operator Type (void) const - { - return (v[0] << 24) - + (v[1] << 16) - + (v[2] << 8) - + (v[3] ); - } - private: uint8_t v[4]; -}; - -/* Integer types in big-endian order and no alignment requirement */ -template -struct IntType -{ - inline void set (Type i) { v.set (i); } - inline operator Type(void) const { return v; } - inline bool operator == (const IntType &o) const { return (Type) v == (Type) o.v; } - inline bool operator != (const IntType &o) const { return !(*this == o); } - static inline int cmp (const IntType *a, const IntType *b) { return b->cmp (*a); } - template - inline int cmp (Type2 a) const - { - Type b = v; - if (sizeof (Type) < sizeof (int) && sizeof (Type2) < sizeof (int)) - return (int) a - (int) b; - else - return a < b ? -1 : a == b ? 0 : +1; - } - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (likely (c->check_struct (this))); - } - protected: - BEInt v; - public: - DEFINE_SIZE_STATIC (Size); -}; - -typedef IntType UINT8; /* 8-bit unsigned integer. */ -typedef IntType INT8; /* 8-bit signed integer. */ -typedef IntType UINT16; /* 16-bit unsigned integer. */ -typedef IntType INT16; /* 16-bit signed integer. */ -typedef IntType UINT32; /* 32-bit unsigned integer. */ -typedef IntType INT32; /* 32-bit signed integer. */ -typedef IntType UINT24; /* 24-bit unsigned integer. */ - -/* 16-bit signed integer (INT16) that describes a quantity in FUnits. */ -typedef INT16 FWORD; - -/* 16-bit unsigned integer (UINT16) that describes a quantity in FUnits. */ -typedef UINT16 UFWORD; - -/* 16-bit signed fixed number with the low 14 bits of fraction (2.14). */ -struct F2DOT14 : INT16 -{ - //inline float to_float (void) const { return ???; } - //inline void set_float (float f) { v.set (f * ???); } - public: - DEFINE_SIZE_STATIC (2); -}; - -/* 32-bit signed fixed-point number (16.16). */ -struct Fixed: INT32 -{ - //inline float to_float (void) const { return ???; } - //inline void set_float (float f) { v.set (f * ???); } - public: - DEFINE_SIZE_STATIC (4); -}; - -/* Date represented in number of seconds since 12:00 midnight, January 1, - * 1904. The value is represented as a signed 64-bit integer. */ -struct LONGDATETIME -{ - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (likely (c->check_struct (this))); - } - protected: - INT32 major; - UINT32 minor; - public: - DEFINE_SIZE_STATIC (8); -}; - -/* Array of four uint8s (length = 32 bits) used to identify a script, language - * system, feature, or baseline */ -struct Tag : UINT32 -{ - /* What the char* converters return is NOT nul-terminated. Print using "%.4s" */ - inline operator const char* (void) const { return reinterpret_cast (&this->v); } - inline operator char* (void) { return reinterpret_cast (&this->v); } - public: - DEFINE_SIZE_STATIC (4); -}; -DEFINE_NULL_DATA (Tag, " "); - -/* Glyph index number, same as uint16 (length = 16 bits) */ -typedef UINT16 GlyphID; - -/* Script/language-system/feature index */ -struct Index : UINT16 { - static const unsigned int NOT_FOUND_INDEX = 0xFFFFu; -}; -DEFINE_NULL_DATA (Index, "\xff\xff"); - -/* Offset, Null offset = 0 */ -template -struct Offset : Type -{ - inline bool is_null (void) const { return 0 == *this; } - public: - DEFINE_SIZE_STATIC (sizeof(Type)); -}; - -typedef Offset Offset16; -typedef Offset Offset32; - - -/* CheckSum */ -struct CheckSum : UINT32 -{ - /* This is reference implementation from the spec. */ - static inline uint32_t CalcTableChecksum (const UINT32 *Table, uint32_t Length) - { - uint32_t Sum = 0L; - const UINT32 *EndPtr = Table+((Length+3) & ~3) / UINT32::static_size; - - while (Table < EndPtr) - Sum += *Table++; - return Sum; - } - - /* Note: data should be 4byte aligned and have 4byte padding at the end. */ - inline void set_for_data (const void *data, unsigned int length) - { set (CalcTableChecksum ((const UINT32 *) data, length)); } - - public: - DEFINE_SIZE_STATIC (4); -}; - - -/* - * Version Numbers - */ - -template -struct FixedVersion -{ - inline uint32_t to_int (void) const { return (major << (sizeof(FixedType) * 8)) + minor; } - - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (c->check_struct (this)); - } - - FixedType major; - FixedType minor; - public: - DEFINE_SIZE_STATIC (2 * sizeof(FixedType)); -}; - - - -/* - * Template subclasses of Offset that do the dereferencing. - * Use: (base+offset) - */ - -template -struct OffsetTo : Offset -{ - inline const Type& operator () (const void *base) const - { - unsigned int offset = *this; - if (unlikely (!offset)) return Null(Type); - return StructAtOffset (base, offset); - } - - inline Type& serialize (hb_serialize_context_t *c, const void *base) - { - Type *t = c->start_embed (); - this->set ((char *) t - (char *) base); /* TODO(serialize) Overflow? */ - return *t; - } - - inline bool sanitize (hb_sanitize_context_t *c, const void *base) const - { - TRACE_SANITIZE (this); - if (unlikely (!c->check_struct (this))) return_trace (false); - unsigned int offset = *this; - if (unlikely (!offset)) return_trace (true); - if (unlikely (!c->check_range (base, offset))) return_trace (false); - const Type &obj = StructAtOffset (base, offset); - return_trace (likely (obj.sanitize (c)) || neuter (c)); - } - template - inline bool sanitize (hb_sanitize_context_t *c, const void *base, T user_data) const - { - TRACE_SANITIZE (this); - if (unlikely (!c->check_struct (this))) return_trace (false); - unsigned int offset = *this; - if (unlikely (!offset)) return_trace (true); - if (unlikely (!c->check_range (base, offset))) return_trace (false); - const Type &obj = StructAtOffset (base, offset); - return_trace (likely (obj.sanitize (c, user_data)) || neuter (c)); - } - - /* Set the offset to Null */ - inline bool neuter (hb_sanitize_context_t *c) const { - return c->try_set (this, 0); - } - DEFINE_SIZE_STATIC (sizeof(OffsetType)); -}; -template struct LOffsetTo : OffsetTo {}; -template -static inline const Type& operator + (const Base &base, const OffsetTo &offset) { return offset (base); } -template -static inline Type& operator + (Base &base, OffsetTo &offset) { return offset (base); } - - -/* - * Array Types - */ - -/* An array with a number of elements. */ -template -struct ArrayOf -{ - const Type *sub_array (unsigned int start_offset, unsigned int *pcount /* IN/OUT */) const - { - unsigned int count = len; - if (unlikely (start_offset > count)) - count = 0; - else - count -= start_offset; - count = MIN (count, *pcount); - *pcount = count; - return array + start_offset; - } - - inline const Type& operator [] (unsigned int i) const - { - if (unlikely (i >= len)) return Null(Type); - return array[i]; - } - inline Type& operator [] (unsigned int i) - { - return array[i]; - } - inline unsigned int get_size (void) const - { return len.static_size + len * Type::static_size; } - - inline bool serialize (hb_serialize_context_t *c, - unsigned int items_len) - { - TRACE_SERIALIZE (this); - if (unlikely (!c->extend_min (*this))) return_trace (false); - len.set (items_len); /* TODO(serialize) Overflow? */ - if (unlikely (!c->extend (*this))) return_trace (false); - return_trace (true); - } - - inline bool serialize (hb_serialize_context_t *c, - Supplier &items, - unsigned int items_len) - { - TRACE_SERIALIZE (this); - if (unlikely (!serialize (c, items_len))) return_trace (false); - for (unsigned int i = 0; i < items_len; i++) - array[i] = items[i]; - items.advance (items_len); - return_trace (true); - } - - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - if (unlikely (!sanitize_shallow (c))) return_trace (false); - - /* Note: for structs that do not reference other structs, - * we do not need to call their sanitize() as we already did - * a bound check on the aggregate array size. We just include - * a small unreachable expression to make sure the structs - * pointed to do have a simple sanitize(), ie. they do not - * reference other structs via offsets. - */ - (void) (false && array[0].sanitize (c)); - - return_trace (true); - } - inline bool sanitize (hb_sanitize_context_t *c, const void *base) 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 (!array[i].sanitize (c, base))) - return_trace (false); - return_trace (true); - } - template - inline 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 (!array[i].sanitize (c, base, user_data))) - return_trace (false); - return_trace (true); - } - - template - inline int lsearch (const SearchType &x) const - { - unsigned int count = len; - for (unsigned int i = 0; i < count; i++) - if (!this->array[i].cmp (x)) - return i; - return -1; - } - - private: - inline bool sanitize_shallow (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (len.sanitize (c) && c->check_array (array, Type::static_size, len)); - } - - public: - LenType len; - Type array[VAR]; - public: - DEFINE_SIZE_ARRAY (sizeof (LenType), array); -}; -template struct LArrayOf : ArrayOf {}; - -/* Array of Offset's */ -template -struct OffsetArrayOf : ArrayOf > {}; - -/* Array of offsets relative to the beginning of the array itself. */ -template -struct OffsetListOf : OffsetArrayOf -{ - inline const Type& operator [] (unsigned int i) const - { - if (unlikely (i >= this->len)) return Null(Type); - return this+this->array[i]; - } - - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (OffsetArrayOf::sanitize (c, this)); - } - template - inline bool sanitize (hb_sanitize_context_t *c, T user_data) const - { - TRACE_SANITIZE (this); - return_trace (OffsetArrayOf::sanitize (c, this, user_data)); - } -}; - - -/* An array starting at second element. */ -template -struct HeadlessArrayOf -{ - inline const Type& operator [] (unsigned int i) const - { - if (unlikely (i >= len || !i)) return Null(Type); - return array[i-1]; - } - inline unsigned int get_size (void) const - { return len.static_size + (len ? len - 1 : 0) * Type::static_size; } - - inline bool serialize (hb_serialize_context_t *c, - Supplier &items, - unsigned int items_len) - { - TRACE_SERIALIZE (this); - if (unlikely (!c->extend_min (*this))) return_trace (false); - len.set (items_len); /* TODO(serialize) Overflow? */ - if (unlikely (!items_len)) return_trace (true); - if (unlikely (!c->extend (*this))) return_trace (false); - for (unsigned int i = 0; i < items_len - 1; i++) - array[i] = items[i]; - items.advance (items_len - 1); - return_trace (true); - } - - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - if (unlikely (!sanitize_shallow (c))) return_trace (false); - - /* Note: for structs that do not reference other structs, - * we do not need to call their sanitize() as we already did - * a bound check on the aggregate array size. We just include - * a small unreachable expression to make sure the structs - * pointed to do have a simple sanitize(), ie. they do not - * reference other structs via offsets. - */ - (void) (false && array[0].sanitize (c)); - - return_trace (true); - } - - private: - inline bool sanitize_shallow (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (len.sanitize (c) && - (!len || c->check_array (array, Type::static_size, len - 1))); - } - - public: - LenType len; - Type array[VAR]; - public: - DEFINE_SIZE_ARRAY (sizeof (LenType), array); -}; - - -/* - * An array with sorted elements. Supports binary searching. - */ -template -struct SortedArrayOf : ArrayOf -{ - template - inline int bsearch (const SearchType &x) const - { - /* Hand-coded bsearch here since this is in the hot inner loop. */ - const Type *array = this->array; - int min = 0, max = (int) this->len - 1; - while (min <= max) - { - int mid = (min + max) / 2; - int c = array[mid].cmp (x); - if (c < 0) - max = mid - 1; - else if (c > 0) - min = mid + 1; - else - return mid; - } - return -1; - } -}; - -/* - * Binary-search arrays - */ - -struct BinSearchHeader -{ - inline operator uint32_t (void) const { return len; } - - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (c->check_struct (this)); - } - - protected: - UINT16 len; - UINT16 searchRangeZ; - UINT16 entrySelectorZ; - UINT16 rangeShiftZ; - - public: - DEFINE_SIZE_STATIC (8); -}; - -template -struct BinSearchArrayOf : SortedArrayOf {}; - - -/* Lazy struct and blob loaders. */ - -/* Logic is shared between hb_lazy_loader_t and hb_lazy_table_loader_t */ -template -struct hb_lazy_loader_t -{ - inline void init (hb_face_t *face_) - { - face = face_; - instance = nullptr; - } - - inline void fini (void) - { - if (instance && instance != &OT::Null(T)) - { - instance->fini(); - free (instance); - } - } - - inline const T* get (void) const - { - retry: - T *p = (T *) hb_atomic_ptr_get (&instance); - if (unlikely (!p)) - { - p = (T *) calloc (1, sizeof (T)); - if (unlikely (!p)) - p = const_cast (&OT::Null(T)); - else - p->init (face); - if (unlikely (!hb_atomic_ptr_cmpexch (const_cast(&instance), nullptr, p))) - { - if (p != &OT::Null(T)) - p->fini (); - goto retry; - } - } - return p; - } - - inline const T* operator-> (void) const - { - return get (); - } - - private: - hb_face_t *face; - T *instance; -}; - -/* Logic is shared between hb_lazy_loader_t and hb_lazy_table_loader_t */ -template -struct hb_lazy_table_loader_t -{ - inline void init (hb_face_t *face_) - { - face = face_; - instance = nullptr; - blob = nullptr; - } - - inline void fini (void) - { - hb_blob_destroy (blob); - } - - inline const T* get (void) const - { - retry: - T *p = (T *) hb_atomic_ptr_get (&instance); - if (unlikely (!p)) - { - hb_blob_t *blob_ = OT::Sanitizer::sanitize (face->reference_table (T::tableTag)); - p = const_cast(OT::Sanitizer::lock_instance (blob_)); - if (!hb_atomic_ptr_cmpexch (const_cast(&instance), nullptr, p)) - { - hb_blob_destroy (blob_); - goto retry; - } - blob = blob_; - } - return p; - } - - inline const T* operator-> (void) const - { - return get(); - } - - private: - hb_face_t *face; - T *instance; - mutable hb_blob_t *blob; -}; - - -} /* namespace OT */ - - -#endif /* HB_OPEN_TYPE_PRIVATE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-open-type.hh b/src/3rdparty/harfbuzz-ng/src/hb-open-type.hh new file mode 100644 index 00000000000..d3fdd1caf52 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-open-type.hh @@ -0,0 +1,1150 @@ +/* + * Copyright © 2007,2008,2009,2010 Red Hat, Inc. + * Copyright © 2012 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OPEN_TYPE_HH +#define HB_OPEN_TYPE_HH + +#include "hb.hh" +#include "hb-blob.hh" +#include "hb-face.hh" +#include "hb-machinery.hh" +#include "hb-meta.hh" +#include "hb-subset.hh" + + +namespace OT { + + +/* + * + * The OpenType Font File: Data Types + */ + + +/* "The following data types are used in the OpenType font file. + * All OpenType fonts use Motorola-style byte ordering (Big Endian):" */ + +/* + * Int types + */ + +/* Integer types in big-endian order and no alignment requirement */ +template +struct IntType +{ + typedef Type type; + + IntType () = default; + explicit constexpr IntType (Type V) : v {V} {} + IntType& operator = (Type i) { v = i; return *this; } + /* For reason we define cast out operator for signed/unsigned, instead of Type, see: + * https://github.com/harfbuzz/harfbuzz/pull/2875/commits/09836013995cab2b9f07577a179ad7b024130467 */ + operator typename std::conditional::value, signed, unsigned>::type () const { return v; } + + bool operator == (const IntType &o) const { return (Type) v == (Type) o.v; } + bool operator != (const IntType &o) const { return !(*this == o); } + + IntType& operator += (unsigned count) { *this = *this + count; return *this; } + IntType& operator -= (unsigned count) { *this = *this - count; return *this; } + IntType& operator ++ () { *this += 1; return *this; } + IntType& operator -- () { *this -= 1; return *this; } + IntType operator ++ (int) { IntType c (*this); ++*this; return c; } + IntType operator -- (int) { IntType c (*this); --*this; return c; } + + HB_INTERNAL static int cmp (const IntType *a, const IntType *b) + { return b->cmp (*a); } + HB_INTERNAL static int cmp (const void *a, const void *b) + { + IntType *pa = (IntType *) a; + IntType *pb = (IntType *) b; + + return pb->cmp (*pa); + } + template ::value && + sizeof (Type2) < sizeof (int) && + sizeof (Type) < sizeof (int))> + int cmp (Type2 a) const + { + Type b = v; + return (int) a - (int) b; + } + template + int cmp (Type2 a) const + { + Type b = v; + return a < b ? -1 : a == b ? 0 : +1; + } + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + protected: + BEInt v; + public: + DEFINE_SIZE_STATIC (Size); +}; + +typedef IntType HBUINT8; /* 8-bit unsigned integer. */ +typedef IntType HBINT8; /* 8-bit signed integer. */ +typedef IntType HBUINT16; /* 16-bit unsigned integer. */ +typedef IntType HBINT16; /* 16-bit signed integer. */ +typedef IntType HBUINT32; /* 32-bit unsigned integer. */ +typedef IntType HBINT32; /* 32-bit signed integer. */ +/* Note: we cannot defined a signed HBINT24 because there's no corresponding C type. + * Works for unsigned, but not signed, since we rely on compiler for sign-extension. */ +typedef IntType HBUINT24; /* 24-bit unsigned integer. */ + +/* 15-bit unsigned number; top bit used for extension. */ +struct HBUINT15 : HBUINT16 +{ + /* TODO Flesh out; actually mask top bit. */ + HBUINT15& operator = (uint16_t i ) { HBUINT16::operator= (i); return *this; } + public: + DEFINE_SIZE_STATIC (2); +}; + +/* 16-bit signed integer (HBINT16) that describes a quantity in FUnits. */ +typedef HBINT16 FWORD; + +/* 32-bit signed integer (HBINT32) that describes a quantity in FUnits. */ +typedef HBINT32 FWORD32; + +/* 16-bit unsigned integer (HBUINT16) that describes a quantity in FUnits. */ +typedef HBUINT16 UFWORD; + +template +struct HBFixed : Type +{ + static constexpr float shift = (float) (1 << fraction_bits); + static_assert (Type::static_size * 8 > fraction_bits, ""); + + operator signed () const = delete; + operator unsigned () const = delete; + typename Type::type to_int () const { return Type::v; } + void set_int (typename Type::type i ) { Type::v = i; } + float to_float (float offset = 0) const { return ((int32_t) Type::v + offset) / shift; } + void set_float (float f) { Type::v = roundf (f * shift); } + public: + DEFINE_SIZE_STATIC (Type::static_size); +}; + +/* 16-bit signed fixed number with the low 14 bits of fraction (2.14). */ +using F2DOT14 = HBFixed; +using F4DOT12 = HBFixed; +using F6DOT10 = HBFixed; + +/* 32-bit signed fixed-point number (16.16). */ +using F16DOT16 = HBFixed; + +/* Date represented in number of seconds since 12:00 midnight, January 1, + * 1904. The value is represented as a signed 64-bit integer. */ +struct LONGDATETIME +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + protected: + HBINT32 major; + HBUINT32 minor; + public: + DEFINE_SIZE_STATIC (8); +}; + +/* Array of four uint8s (length = 32 bits) used to identify a script, language + * system, feature, or baseline */ +struct Tag : HBUINT32 +{ + Tag& operator = (hb_tag_t i) { HBUINT32::operator= (i); return *this; } + /* What the char* converters return is NOT nul-terminated. Print using "%.4s" */ + operator const char* () const { return reinterpret_cast (this); } + operator char* () { return reinterpret_cast (this); } + public: + DEFINE_SIZE_STATIC (4); +}; + +/* Glyph index number, same as uint16 (length = 16 bits) */ +struct HBGlyphID16 : HBUINT16 +{ + HBGlyphID16& operator = (uint16_t i) { HBUINT16::operator= (i); return *this; } +}; +struct HBGlyphID24 : HBUINT24 +{ + HBGlyphID24& operator = (uint32_t i) { HBUINT24::operator= (i); return *this; } +}; + +/* Script/language-system/feature index */ +struct Index : HBUINT16 { + static constexpr unsigned NOT_FOUND_INDEX = 0xFFFFu; + Index& operator = (uint16_t i) { HBUINT16::operator= (i); return *this; } +}; +DECLARE_NULL_NAMESPACE_BYTES (OT, Index); + +typedef Index NameID; + +struct VarIdx : HBUINT32 { + static constexpr unsigned NO_VARIATION = 0xFFFFFFFFu; + static_assert (NO_VARIATION == HB_OT_LAYOUT_NO_VARIATIONS_INDEX, ""); + static uint32_t add (uint32_t i, unsigned short v) + { + if (i == NO_VARIATION) return i; + return i + v; + } + VarIdx& operator = (uint32_t i) { HBUINT32::operator= (i); return *this; } +}; +DECLARE_NULL_NAMESPACE_BYTES (OT, VarIdx); + +/* Offset, Null offset = 0 */ +template +struct Offset : Type +{ + Offset& operator = (typename Type::type i) { Type::operator= (i); return *this; } + + typedef Type type; + + bool is_null () const { return has_null && 0 == *this; } + + public: + DEFINE_SIZE_STATIC (sizeof (Type)); +}; + +typedef Offset Offset16; +typedef Offset Offset24; +typedef Offset Offset32; + + +/* CheckSum */ +struct CheckSum : HBUINT32 +{ + CheckSum& operator = (uint32_t i) { HBUINT32::operator= (i); return *this; } + + /* This is reference implementation from the spec. */ + static uint32_t CalcTableChecksum (const HBUINT32 *Table, uint32_t Length) + { + uint32_t Sum = 0L; + assert (0 == (Length & 3)); + const HBUINT32 *EndPtr = Table + Length / HBUINT32::static_size; + + while (Table < EndPtr) + Sum += *Table++; + return Sum; + } + + /* Note: data should be 4byte aligned and have 4byte padding at the end. */ + void set_for_data (const void *data, unsigned int length) + { *this = CalcTableChecksum ((const HBUINT32 *) data, length); } + + public: + DEFINE_SIZE_STATIC (4); +}; + + +/* + * Version Numbers + */ + +template +struct FixedVersion +{ + uint32_t to_int () const { return (major << (sizeof (FixedType) * 8)) + minor; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + FixedType major; + FixedType minor; + public: + DEFINE_SIZE_STATIC (2 * sizeof (FixedType)); +}; + + +/* + * Template subclasses of Offset that do the dereferencing. + * Use: (base+offset) + */ + +template +struct _hb_has_null +{ + static const Type *get_null () { return nullptr; } + static Type *get_crap () { return nullptr; } +}; +template +struct _hb_has_null +{ + static const Type *get_null () { return &Null (Type); } + static Type *get_crap () { return &Crap (Type); } +}; + +template +struct OffsetTo : Offset +{ + using target_t = Type; + + // Make sure Type is not unbounded; works only for types that are fully defined at OffsetTo time. + static_assert (has_null == false || + (hb_has_null_size (Type) || !hb_has_min_size (Type)), ""); + + HB_DELETE_COPY_ASSIGN (OffsetTo); + OffsetTo () = default; + + OffsetTo& operator = (typename OffsetType::type i) { OffsetType::operator= (i); return *this; } + + const Type& operator () (const void *base) const + { + if (unlikely (this->is_null ())) return *_hb_has_null::get_null (); + return StructAtOffset (base, *this); + } + Type& operator () (void *base) const + { + if (unlikely (this->is_null ())) return *_hb_has_null::get_crap (); + return StructAtOffset (base, *this); + } + + template + friend const Type& operator + (const Base &base, const OffsetTo &offset) { return offset ((const void *) base); } + template + friend const Type& operator + (const OffsetTo &offset, const Base &base) { return offset ((const void *) base); } + template + friend Type& operator + (Base &&base, OffsetTo &offset) { return offset ((void *) base); } + template + friend Type& operator + (OffsetTo &offset, Base &&base) { return offset ((void *) base); } + + + template + bool serialize_subset (hb_subset_context_t *c, const OffsetTo& src, + const void *src_base, Ts&&... ds) + { + *this = 0; + if (src.is_null ()) + return false; + + auto *s = c->serializer; + + s->push (); + + bool ret = c->dispatch (src_base+src, std::forward (ds)...); + + if (ret || !has_null) + s->add_link (*this, s->pop_pack ()); + else + s->pop_discard (); + + return ret; + } + + + template + bool serialize_serialize (hb_serialize_context_t *c, Ts&&... ds) + { + *this = 0; + + Type* obj = c->push (); + bool ret = obj->serialize (c, std::forward (ds)...); + + if (ret) + c->add_link (*this, c->pop_pack ()); + else + c->pop_discard (); + + return ret; + } + + /* TODO: Somehow merge this with previous function into a serialize_dispatch(). */ + /* Workaround clang bug: https://bugs.llvm.org/show_bug.cgi?id=23029 + * Can't compile: whence = hb_serialize_context_t::Head followed by Ts&&... + */ + template + bool serialize_copy (hb_serialize_context_t *c, const OffsetTo& src, + const void *src_base, unsigned dst_bias, + hb_serialize_context_t::whence_t whence, + Ts&&... ds) + { + *this = 0; + if (src.is_null ()) + return false; + + c->push (); + + bool ret = c->copy (src_base+src, std::forward (ds)...); + + c->add_link (*this, c->pop_pack (), whence, dst_bias); + + return ret; + } + + bool serialize_copy (hb_serialize_context_t *c, const OffsetTo& src, + const void *src_base, unsigned dst_bias = 0) + { return serialize_copy (c, src, src_base, dst_bias, hb_serialize_context_t::Head); } + + bool sanitize_shallow (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) return_trace (false); + //if (unlikely (this->is_null ())) return_trace (true); + if (unlikely ((const char *) base + (unsigned) *this < (const char *) base)) return_trace (false); + return_trace (true); + } + + template +#ifndef HB_OPTIMIZE_SIZE + HB_ALWAYS_INLINE +#endif + bool sanitize (hb_sanitize_context_t *c, const void *base, Ts&&... ds) const + { + TRACE_SANITIZE (this); + return_trace (sanitize_shallow (c, base) && + (this->is_null () || + c->dispatch (StructAtOffset (base, *this), std::forward (ds)...) || + neuter (c))); + } + + /* Set the offset to Null */ + bool neuter (hb_sanitize_context_t *c) const + { + if (!has_null) return false; + return c->try_set (this, 0); + } + DEFINE_SIZE_STATIC (sizeof (OffsetType)); +}; +/* Partial specializations. */ +template using Offset16To = OffsetTo; +template using Offset24To = OffsetTo; +template using Offset32To = OffsetTo; + +template using NNOffsetTo = OffsetTo; +template using NNOffset16To = Offset16To; +template using NNOffset24To = Offset24To; +template using NNOffset32To = Offset32To; + + +/* + * Array Types + */ + +template +struct UnsizedArrayOf +{ + typedef Type item_t; + static constexpr unsigned item_size = hb_static_size (Type); + + HB_DELETE_CREATE_COPY_ASSIGN (UnsizedArrayOf); + + const Type& operator [] (unsigned int i) const + { + return arrayZ[i]; + } + Type& operator [] (unsigned int i) + { + return arrayZ[i]; + } + + static unsigned int get_size (unsigned int len) + { return len * Type::static_size; } + + template operator T * () { return arrayZ; } + template operator const T * () const { return arrayZ; } + hb_array_t as_array (unsigned int len) + { return hb_array (arrayZ, len); } + hb_array_t as_array (unsigned int len) const + { return hb_array (arrayZ, len); } + + template + Type &lsearch (unsigned int len, const T &x, Type ¬_found = Crap (Type)) + { return *as_array (len).lsearch (x, ¬_found); } + template + const Type &lsearch (unsigned int len, const T &x, const Type ¬_found = Null (Type)) const + { return *as_array (len).lsearch (x, ¬_found); } + template + bool lfind (unsigned int len, const T &x, unsigned int *i = nullptr, + hb_not_found_t not_found = HB_NOT_FOUND_DONT_STORE, + unsigned int to_store = (unsigned int) -1) const + { return as_array (len).lfind (x, i, not_found, to_store); } + + void qsort (unsigned int len, unsigned int start = 0, unsigned int end = (unsigned int) -1) + { as_array (len).qsort (start, end); } + + bool serialize (hb_serialize_context_t *c, unsigned int items_len, bool clear = true) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_size (this, get_size (items_len), clear))) return_trace (false); + return_trace (true); + } + template + bool serialize (hb_serialize_context_t *c, Iterator items) + { + TRACE_SERIALIZE (this); + unsigned count = hb_len (items); + if (unlikely (!serialize (c, count, false))) return_trace (false); + /* TODO Umm. Just exhaust the iterator instead? Being extra + * cautious right now.. */ + for (unsigned i = 0; i < count; i++, ++items) + arrayZ[i] = *items; + return_trace (true); + } + + UnsizedArrayOf* copy (hb_serialize_context_t *c, unsigned count) const + { + TRACE_SERIALIZE (this); + auto *out = c->start_embed (this); + if (unlikely (!as_array (count).copy (c))) return_trace (nullptr); + return_trace (out); + } + + template + HB_ALWAYS_INLINE + bool sanitize (hb_sanitize_context_t *c, unsigned int count, Ts&&... ds) const + { + TRACE_SANITIZE (this); + if (unlikely (!sanitize_shallow (c, count))) return_trace (false); + if (!sizeof... (Ts) && hb_is_trivially_copyable(Type)) return_trace (true); + for (unsigned int i = 0; i < count; i++) + if (unlikely (!c->dispatch (arrayZ[i], std::forward (ds)...))) + return_trace (false); + return_trace (true); + } + + bool sanitize_shallow (hb_sanitize_context_t *c, unsigned int count) const + { + TRACE_SANITIZE (this); + return_trace (c->check_array (arrayZ, count)); + } + + public: + Type arrayZ[HB_VAR_ARRAY]; + public: + DEFINE_SIZE_UNBOUNDED (0); +}; + +/* Unsized array of offset's */ +template +using UnsizedArray16OfOffsetTo = UnsizedArrayOf>; + +/* Unsized array of offsets relative to the beginning of the array itself. */ +template +struct UnsizedListOfOffset16To : UnsizedArray16OfOffsetTo +{ + const Type& operator [] (int i_) const + { + unsigned int i = (unsigned int) i_; + const OffsetTo *p = &this->arrayZ[i]; + if (unlikely ((const void *) p < (const void *) this->arrayZ)) return Null (Type); /* Overflowed. */ + _hb_compiler_memory_r_barrier (); + return this+*p; + } + Type& operator [] (int i_) + { + unsigned int i = (unsigned int) i_; + const OffsetTo *p = &this->arrayZ[i]; + if (unlikely ((const void *) p < (const void *) this->arrayZ)) return Crap (Type); /* Overflowed. */ + _hb_compiler_memory_r_barrier (); + return this+*p; + } + + template + bool sanitize (hb_sanitize_context_t *c, unsigned int count, Ts&&... ds) const + { + TRACE_SANITIZE (this); + return_trace ((UnsizedArray16OfOffsetTo + ::sanitize (c, count, this, std::forward (ds)...))); + } +}; + +/* An array with sorted elements. Supports binary searching. */ +template +struct SortedUnsizedArrayOf : UnsizedArrayOf +{ + hb_sorted_array_t as_array (unsigned int len) + { return hb_sorted_array (this->arrayZ, len); } + hb_sorted_array_t as_array (unsigned int len) const + { return hb_sorted_array (this->arrayZ, len); } + operator hb_sorted_array_t () { return as_array (); } + operator hb_sorted_array_t () const { return as_array (); } + + template + Type &bsearch (unsigned int len, const T &x, Type ¬_found = Crap (Type)) + { return *as_array (len).bsearch (x, ¬_found); } + template + const Type &bsearch (unsigned int len, const T &x, const Type ¬_found = Null (Type)) const + { return *as_array (len).bsearch (x, ¬_found); } + template + bool bfind (unsigned int len, const T &x, unsigned int *i = nullptr, + hb_not_found_t not_found = HB_NOT_FOUND_DONT_STORE, + unsigned int to_store = (unsigned int) -1) const + { return as_array (len).bfind (x, i, not_found, to_store); } +}; + + +/* An array with a number of elements. */ +template +struct ArrayOf +{ + typedef Type item_t; + static constexpr unsigned item_size = hb_static_size (Type); + + HB_DELETE_CREATE_COPY_ASSIGN (ArrayOf); + + const Type& operator [] (int i_) const + { + unsigned int i = (unsigned int) i_; + if (unlikely (i >= len)) return Null (Type); + _hb_compiler_memory_r_barrier (); + return arrayZ[i]; + } + Type& operator [] (int i_) + { + unsigned int i = (unsigned int) i_; + if (unlikely (i >= len)) return Crap (Type); + _hb_compiler_memory_r_barrier (); + return arrayZ[i]; + } + + unsigned int get_size () const + { return len.static_size + len * Type::static_size; } + + explicit operator bool () const { return len; } + + void pop () { len--; } + + hb_array_t< Type> as_array () { return hb_array (arrayZ, len); } + hb_array_t as_array () const { return hb_array (arrayZ, len); } + + /* Iterator. */ + typedef hb_array_t iter_t; + typedef hb_array_t< Type> writer_t; + iter_t iter () const { return as_array (); } + writer_t writer () { return as_array (); } + operator iter_t () const { return iter (); } + operator writer_t () { return writer (); } + + /* Faster range-based for loop. */ + const Type *begin () const { return arrayZ; } + const Type *end () const { return arrayZ + len; } + + template + Type &lsearch (const T &x, Type ¬_found = Crap (Type)) + { return *as_array ().lsearch (x, ¬_found); } + template + const Type &lsearch (const T &x, const Type ¬_found = Null (Type)) const + { return *as_array ().lsearch (x, ¬_found); } + template + bool lfind (const T &x, unsigned int *i = nullptr, + hb_not_found_t not_found = HB_NOT_FOUND_DONT_STORE, + unsigned int to_store = (unsigned int) -1) const + { return as_array ().lfind (x, i, not_found, to_store); } + + void qsort () + { as_array ().qsort (); } + + HB_NODISCARD bool serialize (hb_serialize_context_t *c, unsigned items_len, bool clear = true) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (this))) return_trace (false); + c->check_assign (len, items_len, HB_SERIALIZE_ERROR_ARRAY_OVERFLOW); + if (unlikely (!c->extend_size (this, get_size (), clear))) return_trace (false); + return_trace (true); + } + template + HB_NODISCARD bool serialize (hb_serialize_context_t *c, Iterator items) + { + TRACE_SERIALIZE (this); + unsigned count = hb_len (items); + if (unlikely (!serialize (c, count, false))) return_trace (false); + /* TODO Umm. Just exhaust the iterator instead? Being extra + * cautious right now.. */ + for (unsigned i = 0; i < count; i++, ++items) + arrayZ[i] = *items; + return_trace (true); + } + + Type* serialize_append (hb_serialize_context_t *c) + { + TRACE_SERIALIZE (this); + len++; + if (unlikely (!len || !c->extend (this))) + { + len--; + return_trace (nullptr); + } + return_trace (&arrayZ[len - 1]); + } + + ArrayOf* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + auto *out = c->start_embed (this); + if (unlikely (!c->extend_min (out))) return_trace (nullptr); + c->check_assign (out->len, len, HB_SERIALIZE_ERROR_ARRAY_OVERFLOW); + if (unlikely (!as_array ().copy (c))) return_trace (nullptr); + return_trace (out); + } + + template + HB_ALWAYS_INLINE + bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const + { + TRACE_SANITIZE (this); + if (unlikely (!sanitize_shallow (c))) return_trace (false); + if (!sizeof... (Ts) && hb_is_trivially_copyable(Type)) return_trace (true); + unsigned int count = len; + for (unsigned int i = 0; i < count; i++) + if (unlikely (!c->dispatch (arrayZ[i], std::forward (ds)...))) + return_trace (false); + return_trace (true); + } + + bool sanitize_shallow (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (len.sanitize (c) && c->check_array_sized (arrayZ, len, sizeof (LenType))); + } + + public: + LenType len; + Type arrayZ[HB_VAR_ARRAY]; + public: + DEFINE_SIZE_ARRAY (sizeof (LenType), arrayZ); +}; +template using Array16Of = ArrayOf; +template using Array24Of = ArrayOf; +template using Array32Of = ArrayOf; +using PString = ArrayOf; + +/* Array of Offset's */ +template using Array16OfOffset16To = ArrayOf, HBUINT16>; +template using Array16OfOffset32To = ArrayOf, HBUINT16>; +template using Array32OfOffset32To = ArrayOf, HBUINT32>; + +/* Array of offsets relative to the beginning of the array itself. */ +template +struct List16OfOffsetTo : ArrayOf, HBUINT16> +{ + const Type& operator [] (int i_) const + { + unsigned int i = (unsigned int) i_; + if (unlikely (i >= this->len)) return Null (Type); + _hb_compiler_memory_r_barrier (); + return this+this->arrayZ[i]; + } + const Type& operator [] (int i_) + { + unsigned int i = (unsigned int) i_; + if (unlikely (i >= this->len)) return Crap (Type); + _hb_compiler_memory_r_barrier (); + return this+this->arrayZ[i]; + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + struct List16OfOffsetTo *out = c->serializer->embed (*this); + if (unlikely (!out)) return_trace (false); + unsigned int count = this->len; + for (unsigned int i = 0; i < count; i++) + out->arrayZ[i].serialize_subset (c, this->arrayZ[i], this, out); + return_trace (true); + } + + template + bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const + { + TRACE_SANITIZE (this); + return_trace ((Array16Of>::sanitize (c, this, std::forward (ds)...))); + } +}; + +template +using List16OfOffset16To = List16OfOffsetTo; + +/* An array starting at second element. */ +template +struct HeadlessArrayOf +{ + static constexpr unsigned item_size = Type::static_size; + + HB_DELETE_CREATE_COPY_ASSIGN (HeadlessArrayOf); + + const Type& operator [] (int i_) const + { + unsigned int i = (unsigned int) i_; + if (unlikely (i >= lenP1 || !i)) return Null (Type); + _hb_compiler_memory_r_barrier (); + return arrayZ[i-1]; + } + Type& operator [] (int i_) + { + unsigned int i = (unsigned int) i_; + if (unlikely (i >= lenP1 || !i)) return Crap (Type); + _hb_compiler_memory_r_barrier (); + return arrayZ[i-1]; + } + unsigned int get_size () const + { return lenP1.static_size + get_length () * Type::static_size; } + + unsigned get_length () const { return lenP1 ? lenP1 - 1 : 0; } + + hb_array_t< Type> as_array () { return hb_array (arrayZ, get_length ()); } + hb_array_t as_array () const { return hb_array (arrayZ, get_length ()); } + + /* Iterator. */ + typedef hb_array_t iter_t; + typedef hb_array_t< Type> writer_t; + iter_t iter () const { return as_array (); } + writer_t writer () { return as_array (); } + operator iter_t () const { return iter (); } + operator writer_t () { return writer (); } + + /* Faster range-based for loop. */ + const Type *begin () const { return arrayZ; } + const Type *end () const { return arrayZ + get_length (); } + + HB_NODISCARD bool serialize (hb_serialize_context_t *c, unsigned int items_len, bool clear = true) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (this))) return_trace (false); + c->check_assign (lenP1, items_len + 1, HB_SERIALIZE_ERROR_ARRAY_OVERFLOW); + if (unlikely (!c->extend_size (this, get_size (), clear))) return_trace (false); + return_trace (true); + } + template + HB_NODISCARD bool serialize (hb_serialize_context_t *c, Iterator items) + { + TRACE_SERIALIZE (this); + unsigned count = hb_len (items); + if (unlikely (!serialize (c, count, false))) return_trace (false); + /* TODO Umm. Just exhaust the iterator instead? Being extra + * cautious right now.. */ + for (unsigned i = 0; i < count; i++, ++items) + arrayZ[i] = *items; + return_trace (true); + } + + template + HB_ALWAYS_INLINE + bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const + { + TRACE_SANITIZE (this); + if (unlikely (!sanitize_shallow (c))) return_trace (false); + if (!sizeof... (Ts) && hb_is_trivially_copyable(Type)) return_trace (true); + unsigned int count = get_length (); + for (unsigned int i = 0; i < count; i++) + if (unlikely (!c->dispatch (arrayZ[i], std::forward (ds)...))) + return_trace (false); + return_trace (true); + } + + private: + bool sanitize_shallow (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (lenP1.sanitize (c) && + (!lenP1 || c->check_array_sized (arrayZ, lenP1 - 1, sizeof (LenType)))); + } + + public: + LenType lenP1; + Type arrayZ[HB_VAR_ARRAY]; + public: + DEFINE_SIZE_ARRAY (sizeof (LenType), arrayZ); +}; +template using HeadlessArray16Of = HeadlessArrayOf; + +/* An array storing length-1. */ +template +struct ArrayOfM1 +{ + HB_DELETE_CREATE_COPY_ASSIGN (ArrayOfM1); + + const Type& operator [] (int i_) const + { + unsigned int i = (unsigned int) i_; + if (unlikely (i > lenM1)) return Null (Type); + _hb_compiler_memory_r_barrier (); + return arrayZ[i]; + } + Type& operator [] (int i_) + { + unsigned int i = (unsigned int) i_; + if (unlikely (i > lenM1)) return Crap (Type); + _hb_compiler_memory_r_barrier (); + return arrayZ[i]; + } + unsigned int get_size () const + { return lenM1.static_size + (lenM1 + 1) * Type::static_size; } + + template + HB_ALWAYS_INLINE + bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const + { + TRACE_SANITIZE (this); + if (unlikely (!sanitize_shallow (c))) return_trace (false); + if (!sizeof... (Ts) && hb_is_trivially_copyable(Type)) return_trace (true); + unsigned int count = lenM1 + 1; + for (unsigned int i = 0; i < count; i++) + if (unlikely (!c->dispatch (arrayZ[i], std::forward (ds)...))) + return_trace (false); + return_trace (true); + } + + private: + bool sanitize_shallow (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (lenM1.sanitize (c) && + (c->check_array_sized (arrayZ, lenM1 + 1, sizeof (LenType)))); + } + + public: + LenType lenM1; + Type arrayZ[HB_VAR_ARRAY]; + public: + DEFINE_SIZE_ARRAY (sizeof (LenType), arrayZ); +}; + +/* An array with sorted elements. Supports binary searching. */ +template +struct SortedArrayOf : ArrayOf +{ + hb_sorted_array_t< Type> as_array () { return hb_sorted_array (this->arrayZ, this->len); } + hb_sorted_array_t as_array () const { return hb_sorted_array (this->arrayZ, this->len); } + + /* Iterator. */ + typedef hb_sorted_array_t iter_t; + typedef hb_sorted_array_t< Type> writer_t; + iter_t iter () const { return as_array (); } + writer_t writer () { return as_array (); } + operator iter_t () const { return iter (); } + operator writer_t () { return writer (); } + + /* Faster range-based for loop. */ + const Type *begin () const { return this->arrayZ; } + const Type *end () const { return this->arrayZ + this->len; } + + bool serialize (hb_serialize_context_t *c, unsigned int items_len) + { + TRACE_SERIALIZE (this); + bool ret = ArrayOf::serialize (c, items_len); + return_trace (ret); + } + template + bool serialize (hb_serialize_context_t *c, Iterator items) + { + TRACE_SERIALIZE (this); + bool ret = ArrayOf::serialize (c, items); + return_trace (ret); + } + + template + Type &bsearch (const T &x, Type ¬_found = Crap (Type)) + { return *as_array ().bsearch (x, ¬_found); } + template + const Type &bsearch (const T &x, const Type ¬_found = Null (Type)) const + { return *as_array ().bsearch (x, ¬_found); } + template + bool bfind (const T &x, unsigned int *i = nullptr, + hb_not_found_t not_found = HB_NOT_FOUND_DONT_STORE, + unsigned int to_store = (unsigned int) -1) const + { return as_array ().bfind (x, i, not_found, to_store); } +}; + +template using SortedArray16Of = SortedArrayOf; +template using SortedArray24Of = SortedArrayOf; +template using SortedArray32Of = SortedArrayOf; + +/* + * Binary-search arrays + */ + +template +struct BinSearchHeader +{ + operator uint32_t () const { return len; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + BinSearchHeader& operator = (unsigned int v) + { + len = v; + assert (len == v); + entrySelector = hb_max (1u, hb_bit_storage (v)) - 1; + searchRange = 16 * (1u << entrySelector); + rangeShift = v * 16 > searchRange + ? 16 * v - searchRange + : 0; + return *this; + } + + protected: + LenType len; + LenType searchRange; + LenType entrySelector; + LenType rangeShift; + + public: + DEFINE_SIZE_STATIC (8); +}; + +template +using BinSearchArrayOf = SortedArrayOf>; + + +struct VarSizedBinSearchHeader +{ + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + HBUINT16 unitSize; /* Size of a lookup unit for this search in bytes. */ + HBUINT16 nUnits; /* Number of units of the preceding size to be searched. */ + HBUINT16 searchRange; /* The value of unitSize times the largest power of 2 + * that is less than or equal to the value of nUnits. */ + HBUINT16 entrySelector; /* The log base 2 of the largest power of 2 less than + * or equal to the value of nUnits. */ + HBUINT16 rangeShift; /* The value of unitSize times the difference of the + * value of nUnits minus the largest power of 2 less + * than or equal to the value of nUnits. */ + public: + DEFINE_SIZE_STATIC (10); +}; + +template +struct VarSizedBinSearchArrayOf +{ + static constexpr unsigned item_size = Type::static_size; + + HB_DELETE_CREATE_COPY_ASSIGN (VarSizedBinSearchArrayOf); + + bool last_is_terminator () const + { + if (unlikely (!header.nUnits)) return false; + + /* Gah. + * + * "The number of termination values that need to be included is table-specific. + * The value that indicates binary search termination is 0xFFFF." */ + const HBUINT16 *words = &StructAtOffset (&bytesZ, (header.nUnits - 1) * header.unitSize); + unsigned int count = Type::TerminationWordCount; + for (unsigned int i = 0; i < count; i++) + if (words[i] != 0xFFFFu) + return false; + return true; + } + + const Type& operator [] (int i_) const + { + unsigned int i = (unsigned int) i_; + if (unlikely (i >= get_length ())) return Null (Type); + _hb_compiler_memory_r_barrier (); + return StructAtOffset (&bytesZ, i * header.unitSize); + } + Type& operator [] (int i_) + { + unsigned int i = (unsigned int) i_; + if (unlikely (i >= get_length ())) return Crap (Type); + _hb_compiler_memory_r_barrier (); + return StructAtOffset (&bytesZ, i * header.unitSize); + } + unsigned int get_length () const + { return header.nUnits - last_is_terminator (); } + unsigned int get_size () const + { return header.static_size + header.nUnits * header.unitSize; } + + template + HB_ALWAYS_INLINE + bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const + { + TRACE_SANITIZE (this); + if (unlikely (!sanitize_shallow (c))) return_trace (false); + if (!sizeof... (Ts) && hb_is_trivially_copyable(Type)) return_trace (true); + unsigned int count = get_length (); + for (unsigned int i = 0; i < count; i++) + if (unlikely (!(*this)[i].sanitize (c, std::forward (ds)...))) + return_trace (false); + return_trace (true); + } + + template + const Type *bsearch (const T &key) const + { + unsigned pos; + return hb_bsearch_impl (&pos, + key, + (const void *) bytesZ, + get_length (), + header.unitSize, + _hb_cmp_method) + ? (const Type *) (((const char *) &bytesZ) + (pos * header.unitSize)) + : nullptr; + } + + private: + bool sanitize_shallow (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (header.sanitize (c) && + Type::static_size <= header.unitSize && + c->check_range (bytesZ.arrayZ, + header.nUnits, + header.unitSize)); + } + + protected: + VarSizedBinSearchHeader header; + UnsizedArrayOf bytesZ; + public: + DEFINE_SIZE_ARRAY (10, bytesZ); +}; + + +} /* namespace OT */ + + +#endif /* HB_OPEN_TYPE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-cbdt-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-ot-cbdt-table.hh deleted file mode 100644 index 415625e5f80..00000000000 --- a/src/3rdparty/harfbuzz-ng/src/hb-ot-cbdt-table.hh +++ /dev/null @@ -1,471 +0,0 @@ -/* - * Copyright © 2016 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): Seigo Nonaka - */ - -#ifndef HB_OT_CBDT_TABLE_HH -#define HB_OT_CBDT_TABLE_HH - -#include "hb-open-type-private.hh" - -namespace OT { - -struct SmallGlyphMetrics -{ - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (c->check_struct (this)); - } - - inline void get_extents (hb_glyph_extents_t *extents) const - { - extents->x_bearing = bearingX; - extents->y_bearing = bearingY; - extents->width = width; - extents->height = -height; - } - - UINT8 height; - UINT8 width; - INT8 bearingX; - INT8 bearingY; - UINT8 advance; - - DEFINE_SIZE_STATIC(5); -}; - -struct BigGlyphMetrics : SmallGlyphMetrics -{ - INT8 vertBearingX; - INT8 vertBearingY; - UINT8 vertAdvance; - - DEFINE_SIZE_STATIC(8); -}; - -struct SBitLineMetrics -{ - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (c->check_struct (this)); - } - - INT8 ascender; - INT8 decender; - UINT8 widthMax; - INT8 caretSlopeNumerator; - INT8 caretSlopeDenominator; - INT8 caretOffset; - INT8 minOriginSB; - INT8 minAdvanceSB; - INT8 maxBeforeBL; - INT8 minAfterBL; - INT8 padding1; - INT8 padding2; - - DEFINE_SIZE_STATIC(12); -}; - - -/* - * Index Subtables. - */ - -struct IndexSubtableHeader -{ - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (c->check_struct (this)); - } - - UINT16 indexFormat; - UINT16 imageFormat; - UINT32 imageDataOffset; - - DEFINE_SIZE_STATIC(8); -}; - -template -struct IndexSubtableFormat1Or3 -{ - inline bool sanitize (hb_sanitize_context_t *c, unsigned int glyph_count) const - { - TRACE_SANITIZE (this); - return_trace (c->check_struct (this) && - c->check_array (offsetArrayZ, offsetArrayZ[0].static_size, glyph_count + 1)); - } - - bool get_image_data (unsigned int idx, - unsigned int *offset, - unsigned int *length) const - { - if (unlikely (offsetArrayZ[idx + 1] <= offsetArrayZ[idx])) - return false; - - *offset = header.imageDataOffset + offsetArrayZ[idx]; - *length = offsetArrayZ[idx + 1] - offsetArrayZ[idx]; - return true; - } - - IndexSubtableHeader header; - Offset offsetArrayZ[VAR]; - - DEFINE_SIZE_ARRAY(8, offsetArrayZ); -}; - -struct IndexSubtableFormat1 : IndexSubtableFormat1Or3 {}; -struct IndexSubtableFormat3 : IndexSubtableFormat1Or3 {}; - -struct IndexSubtable -{ - inline bool sanitize (hb_sanitize_context_t *c, unsigned int glyph_count) const - { - TRACE_SANITIZE (this); - if (!u.header.sanitize (c)) return_trace (false); - switch (u.header.indexFormat) { - case 1: return_trace (u.format1.sanitize (c, glyph_count)); - case 3: return_trace (u.format3.sanitize (c, glyph_count)); - default:return_trace (true); - } - } - - inline bool get_extents (hb_glyph_extents_t *extents) const - { - switch (u.header.indexFormat) { - case 2: case 5: /* TODO */ - case 1: case 3: case 4: /* Variable-metrics formats do not have metrics here. */ - default:return (false); - } - } - - bool get_image_data (unsigned int idx, - unsigned int *offset, - unsigned int *length, - unsigned int *format) const - { - *format = u.header.imageFormat; - switch (u.header.indexFormat) { - case 1: return u.format1.get_image_data (idx, offset, length); - case 3: return u.format3.get_image_data (idx, offset, length); - default: return false; - } - } - - protected: - union { - IndexSubtableHeader header; - IndexSubtableFormat1 format1; - IndexSubtableFormat3 format3; - /* TODO: Format 2, 4, 5. */ - } u; - public: - DEFINE_SIZE_UNION (8, header); -}; - -struct IndexSubtableRecord -{ - inline bool sanitize (hb_sanitize_context_t *c, const void *base) const - { - TRACE_SANITIZE (this); - return_trace (c->check_struct (this) && - firstGlyphIndex <= lastGlyphIndex && - offsetToSubtable.sanitize (c, this, lastGlyphIndex - firstGlyphIndex + 1)); - } - - inline bool get_extents (hb_glyph_extents_t *extents) const - { - return (this+offsetToSubtable).get_extents (extents); - } - - bool get_image_data (unsigned int gid, - unsigned int *offset, - unsigned int *length, - unsigned int *format) const - { - if (gid < firstGlyphIndex || gid > lastGlyphIndex) - { - return false; - } - return (this+offsetToSubtable).get_image_data (gid - firstGlyphIndex, - offset, length, format); - } - - UINT16 firstGlyphIndex; - UINT16 lastGlyphIndex; - LOffsetTo offsetToSubtable; - - DEFINE_SIZE_STATIC(8); -}; - -struct IndexSubtableArray -{ - inline bool sanitize (hb_sanitize_context_t *c, unsigned int count) const - { - TRACE_SANITIZE (this); - if (unlikely (!c->check_array (&indexSubtablesZ, indexSubtablesZ[0].static_size, count))) - return_trace (false); - for (unsigned int i = 0; i < count; i++) - if (unlikely (!indexSubtablesZ[i].sanitize (c, this))) - return_trace (false); - return_trace (true); - } - - public: - const IndexSubtableRecord* find_table (hb_codepoint_t glyph, unsigned int numTables) const - { - for (unsigned int i = 0; i < numTables; ++i) - { - unsigned int firstGlyphIndex = indexSubtablesZ[i].firstGlyphIndex; - unsigned int lastGlyphIndex = indexSubtablesZ[i].lastGlyphIndex; - if (firstGlyphIndex <= glyph && glyph <= lastGlyphIndex) { - return &indexSubtablesZ[i]; - } - } - return nullptr; - } - - protected: - IndexSubtableRecord indexSubtablesZ[VAR]; - - public: - DEFINE_SIZE_ARRAY(0, indexSubtablesZ); -}; - -struct BitmapSizeTable -{ - friend struct CBLC; - - inline bool sanitize (hb_sanitize_context_t *c, const void *base) const - { - TRACE_SANITIZE (this); - return_trace (c->check_struct (this) && - indexSubtableArrayOffset.sanitize (c, base, numberOfIndexSubtables) && - c->check_range (&(base+indexSubtableArrayOffset), indexTablesSize) && - horizontal.sanitize (c) && - vertical.sanitize (c)); - } - - const IndexSubtableRecord *find_table (hb_codepoint_t glyph, const void *base) const - { - return (base+indexSubtableArrayOffset).find_table (glyph, numberOfIndexSubtables); - } - - protected: - LOffsetTo indexSubtableArrayOffset; - UINT32 indexTablesSize; - UINT32 numberOfIndexSubtables; - UINT32 colorRef; - SBitLineMetrics horizontal; - SBitLineMetrics vertical; - UINT16 startGlyphIndex; - UINT16 endGlyphIndex; - UINT8 ppemX; - UINT8 ppemY; - UINT8 bitDepth; - INT8 flags; - - public: - DEFINE_SIZE_STATIC(48); -}; - - -/* - * Glyph Bitmap Data Formats. - */ - -struct GlyphBitmapDataFormat17 -{ - SmallGlyphMetrics glyphMetrics; - UINT32 dataLen; - UINT8 dataZ[VAR]; - - DEFINE_SIZE_ARRAY(9, dataZ); -}; - - -/* - * CBLC -- Color Bitmap Location Table - */ - -#define HB_OT_TAG_CBLC HB_TAG('C','B','L','C') - -struct CBLC -{ - friend struct CBDT; - - static const hb_tag_t tableTag = HB_OT_TAG_CBLC; - - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (c->check_struct (this) && - likely (version.major == 2 || version.major == 3) && - sizeTables.sanitize (c, this)); - } - - protected: - const IndexSubtableRecord *find_table (hb_codepoint_t glyph, - unsigned int *x_ppem, unsigned int *y_ppem) const - { - /* TODO: Make it possible to select strike. */ - - unsigned int count = sizeTables.len; - for (uint32_t i = 0; i < count; ++i) - { - unsigned int startGlyphIndex = sizeTables.array[i].startGlyphIndex; - unsigned int endGlyphIndex = sizeTables.array[i].endGlyphIndex; - if (startGlyphIndex <= glyph && glyph <= endGlyphIndex) - { - *x_ppem = sizeTables[i].ppemX; - *y_ppem = sizeTables[i].ppemY; - return sizeTables[i].find_table (glyph, this); - } - } - - return nullptr; - } - - protected: - FixedVersion<> version; - LArrayOf sizeTables; - - public: - DEFINE_SIZE_ARRAY(8, sizeTables); -}; - -/* - * CBDT -- Color Bitmap Data Table - */ -#define HB_OT_TAG_CBDT HB_TAG('C','B','D','T') - -struct CBDT -{ - static const hb_tag_t tableTag = HB_OT_TAG_CBDT; - - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (c->check_struct (this) && - likely (version.major == 2 || version.major == 3)); - } - - struct accelerator_t - { - inline void init (hb_face_t *face) - { - upem = face->get_upem(); - - cblc_blob = Sanitizer::sanitize (face->reference_table (HB_OT_TAG_CBLC)); - cbdt_blob = Sanitizer::sanitize (face->reference_table (HB_OT_TAG_CBDT)); - cbdt_len = hb_blob_get_length (cbdt_blob); - - if (hb_blob_get_length (cblc_blob) == 0) { - cblc = nullptr; - cbdt = nullptr; - return; /* Not a bitmap font. */ - } - cblc = Sanitizer::lock_instance (cblc_blob); - cbdt = Sanitizer::lock_instance (cbdt_blob); - - } - - inline void fini (void) - { - hb_blob_destroy (this->cblc_blob); - hb_blob_destroy (this->cbdt_blob); - } - - inline bool get_extents (hb_codepoint_t glyph, hb_glyph_extents_t *extents) const - { - unsigned int x_ppem = upem, y_ppem = upem; /* TODO Use font ppem if available. */ - - if (!cblc) - return false; // Not a color bitmap font. - - const IndexSubtableRecord *subtable_record = this->cblc->find_table(glyph, &x_ppem, &y_ppem); - if (!subtable_record || !x_ppem || !y_ppem) - return false; - - if (subtable_record->get_extents (extents)) - return true; - - unsigned int image_offset = 0, image_length = 0, image_format = 0; - if (!subtable_record->get_image_data (glyph, &image_offset, &image_length, &image_format)) - return false; - - { - if (unlikely (image_offset > cbdt_len || cbdt_len - image_offset < image_length)) - return false; - - switch (image_format) - { - case 17: { - if (unlikely (image_length < GlyphBitmapDataFormat17::min_size)) - return false; - - const GlyphBitmapDataFormat17& glyphFormat17 = - StructAtOffset (this->cbdt, image_offset); - glyphFormat17.glyphMetrics.get_extents (extents); - } - break; - default: - // TODO: Support other image formats. - return false; - } - } - - /* Convert to the font units. */ - extents->x_bearing *= upem / (float) x_ppem; - extents->y_bearing *= upem / (float) y_ppem; - extents->width *= upem / (float) x_ppem; - extents->height *= upem / (float) y_ppem; - - return true; - } - - private: - hb_blob_t *cblc_blob; - hb_blob_t *cbdt_blob; - const CBLC *cblc; - const CBDT *cbdt; - - unsigned int cbdt_len; - unsigned int upem; - }; - - - protected: - FixedVersion<>version; - UINT8 dataZ[VAR]; - - public: - DEFINE_SIZE_ARRAY(4, dataZ); -}; - -} /* namespace OT */ - -#endif /* HB_OT_CBDT_TABLE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-cff-common.hh b/src/3rdparty/harfbuzz-ng/src/hb-ot-cff-common.hh new file mode 100644 index 00000000000..923a32b26f8 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-cff-common.hh @@ -0,0 +1,589 @@ +/* + * Copyright © 2018 Adobe 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. + * + * Adobe Author(s): Michiharu Ariza + */ +#ifndef HB_OT_CFF_COMMON_HH +#define HB_OT_CFF_COMMON_HH + +#include "hb-open-type.hh" +#include "hb-bimap.hh" +#include "hb-ot-layout-common.hh" +#include "hb-cff-interp-dict-common.hh" +#include "hb-subset-plan.hh" + +namespace CFF { + +using namespace OT; + +#define CFF_UNDEF_CODE 0xFFFFFFFF + +using objidx_t = hb_serialize_context_t::objidx_t; +using whence_t = hb_serialize_context_t::whence_t; + +/* utility macro */ +template +static inline const Type& StructAtOffsetOrNull (const void *P, unsigned int offset) +{ return offset ? StructAtOffset (P, offset) : Null (Type); } + +struct code_pair_t +{ + unsigned code; + hb_codepoint_t glyph; +}; + + +using str_buff_t = hb_vector_t; +using str_buff_vec_t = hb_vector_t; +using glyph_to_sid_map_t = hb_vector_t; + +struct length_f_t +{ + template + unsigned operator () (const Iterable &_) const { return hb_len (hb_iter (_)); } + + unsigned operator () (unsigned _) const { return _; } +} +HB_FUNCOBJ (length_f); + +/* CFF INDEX */ +template +struct CFFIndex +{ + unsigned int offset_array_size () const + { return offSize * (count + 1); } + + template + bool serialize (hb_serialize_context_t *c, + const Iterable &iterable, + const unsigned *p_data_size = nullptr) + { + TRACE_SERIALIZE (this); + unsigned data_size; + if (p_data_size) + data_size = *p_data_size; + else + total_size (iterable, &data_size); + + auto it = hb_iter (iterable); + if (unlikely (!serialize_header (c, +it, data_size))) return_trace (false); + unsigned char *ret = c->allocate_size (data_size, false); + if (unlikely (!ret)) return_trace (false); + for (const auto &_ : +it) + { + unsigned len = _.length; + if (!len) + continue; + if (len <= 1) + { + *ret++ = *_.arrayZ; + continue; + } + hb_memcpy (ret, _.arrayZ, len); + ret += len; + } + return_trace (true); + } + + template + bool serialize_header (hb_serialize_context_t *c, + Iterator it, + unsigned data_size) + { + TRACE_SERIALIZE (this); + + unsigned off_size = (hb_bit_storage (data_size + 1) + 7) / 8; + + /* serialize CFFIndex header */ + if (unlikely (!c->extend_min (this))) return_trace (false); + this->count = hb_len (it); + if (!this->count) return_trace (true); + if (unlikely (!c->extend (this->offSize))) return_trace (false); + this->offSize = off_size; + if (unlikely (!c->allocate_size (off_size * (this->count + 1), false))) + return_trace (false); + + /* serialize indices */ + unsigned int offset = 1; + if (HB_OPTIMIZE_SIZE_VAL) + { + unsigned int i = 0; + for (const auto &_ : +it) + { + set_offset_at (i++, offset); + offset += length_f (_); + } + set_offset_at (i, offset); + } + else + switch (off_size) + { + case 1: + { + HBUINT8 *p = (HBUINT8 *) offsets; + for (const auto &_ : +it) + { + *p++ = offset; + offset += length_f (_); + } + *p = offset; + } + break; + case 2: + { + HBUINT16 *p = (HBUINT16 *) offsets; + for (const auto &_ : +it) + { + *p++ = offset; + offset += length_f (_); + } + *p = offset; + } + break; + case 3: + { + HBUINT24 *p = (HBUINT24 *) offsets; + for (const auto &_ : +it) + { + *p++ = offset; + offset += length_f (_); + } + *p = offset; + } + break; + case 4: + { + HBUINT32 *p = (HBUINT32 *) offsets; + for (const auto &_ : +it) + { + *p++ = offset; + offset += length_f (_); + } + *p = offset; + } + break; + default: + break; + } + + assert (offset == data_size + 1); + return_trace (true); + } + + template + static unsigned total_size (const Iterable &iterable, unsigned *data_size = nullptr) + { + auto it = + hb_iter (iterable); + if (!it) + { + if (data_size) *data_size = 0; + return min_size; + } + + unsigned total = 0; + for (const auto &_ : +it) + total += length_f (_); + + if (data_size) *data_size = total; + + unsigned off_size = (hb_bit_storage (total + 1) + 7) / 8; + + return min_size + HBUINT8::static_size + (hb_len (it) + 1) * off_size + total; + } + + void set_offset_at (unsigned int index, unsigned int offset) + { + assert (index <= count); + + unsigned int size = offSize; + const HBUINT8 *p = offsets; + switch (size) + { + case 1: ((HBUINT8 *) p)[index] = offset; break; + case 2: ((HBUINT16 *) p)[index] = offset; break; + case 3: ((HBUINT24 *) p)[index] = offset; break; + case 4: ((HBUINT32 *) p)[index] = offset; break; + default: return; + } + } + + private: + unsigned int offset_at (unsigned int index) const + { + assert (index <= count); + + unsigned int size = offSize; + const HBUINT8 *p = offsets; + switch (size) + { + case 1: return ((HBUINT8 *) p)[index]; + case 2: return ((HBUINT16 *) p)[index]; + case 3: return ((HBUINT24 *) p)[index]; + case 4: return ((HBUINT32 *) p)[index]; + default: return 0; + } + } + + const unsigned char *data_base () const + { return (const unsigned char *) this + min_size + offSize.static_size - 1 + offset_array_size (); } + public: + + hb_ubytes_t operator [] (unsigned int index) const + { + if (unlikely (index >= count)) return hb_ubytes_t (); + _hb_compiler_memory_r_barrier (); + unsigned offset0 = offset_at (index); + unsigned offset1 = offset_at (index + 1); + if (unlikely (offset1 < offset0 || offset1 > offset_at (count))) + return hb_ubytes_t (); + return hb_ubytes_t (data_base () + offset0, offset1 - offset0); + } + + unsigned int get_size () const + { + if (count) + return min_size + offSize.static_size + offset_array_size () + (offset_at (count) - 1); + return min_size; /* empty CFFIndex contains count only */ + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + (count == 0 || /* empty INDEX */ + (count < count + 1u && + c->check_struct (&offSize) && offSize >= 1 && offSize <= 4 && + c->check_array (offsets, offSize, count + 1u) && + c->check_array ((const HBUINT8*) data_base (), 1, offset_at (count)))))); + } + + public: + COUNT count; /* Number of object data. Note there are (count+1) offsets */ + private: + HBUINT8 offSize; /* The byte size of each offset in the offsets array. */ + HBUINT8 offsets[HB_VAR_ARRAY]; + /* The array of (count + 1) offsets into objects array (1-base). */ + /* HBUINT8 data[HB_VAR_ARRAY]; Object data */ + public: + DEFINE_SIZE_MIN (COUNT::static_size); +}; + +/* Top Dict, Font Dict, Private Dict */ +struct Dict : UnsizedByteStr +{ + template + bool serialize (hb_serialize_context_t *c, + const DICTVAL &dictval, + OP_SERIALIZER& opszr, + Ts&&... ds) + { + TRACE_SERIALIZE (this); + for (unsigned int i = 0; i < dictval.get_count (); i++) + if (unlikely (!opszr.serialize (c, dictval[i], std::forward (ds)...))) + return_trace (false); + + return_trace (true); + } + + template + static bool serialize_int_op (hb_serialize_context_t *c, op_code_t op, V value, op_code_t intOp) + { + if (unlikely ((!serialize_int (c, intOp, value)))) + return false; + + TRACE_SERIALIZE (this); + /* serialize the opcode */ + HBUINT8 *p = c->allocate_size (OpCode_Size (op), false); + if (unlikely (!p)) return_trace (false); + if (Is_OpCode_ESC (op)) + { + *p = OpCode_escape; + op = Unmake_OpCode_ESC (op); + p++; + } + *p = op; + return_trace (true); + } + + template + static bool serialize_int4_op (hb_serialize_context_t *c, op_code_t op, V value) + { return serialize_int_op (c, op, value, OpCode_longintdict); } + + template + static bool serialize_int2_op (hb_serialize_context_t *c, op_code_t op, V value) + { return serialize_int_op (c, op, value, OpCode_shortint); } + + template + static bool serialize_link_op (hb_serialize_context_t *c, op_code_t op, objidx_t link, whence_t whence) + { + T &ofs = *(T *) (c->head + OpCode_Size (int_op)); + if (unlikely (!serialize_int_op (c, op, 0, int_op))) return false; + c->add_link (ofs, link, whence); + return true; + } + + static bool serialize_link4_op (hb_serialize_context_t *c, op_code_t op, objidx_t link, whence_t whence = whence_t::Head) + { return serialize_link_op (c, op, link, whence); } + + static bool serialize_link2_op (hb_serialize_context_t *c, op_code_t op, objidx_t link, whence_t whence = whence_t::Head) + { return serialize_link_op (c, op, link, whence); } +}; + +struct TopDict : Dict {}; +struct FontDict : Dict {}; +struct PrivateDict : Dict {}; + +struct table_info_t +{ + void init () { offset = size = 0; link = 0; } + + unsigned int offset; + unsigned int size; + objidx_t link; +}; + +template +struct FDArray : CFFIndex +{ + template + bool serialize (hb_serialize_context_t *c, + Iterator it, + OP_SERIALIZER& opszr) + { + TRACE_SERIALIZE (this); + + /* serialize INDEX data */ + hb_vector_t sizes; + if (it.is_random_access_iterator) + sizes.alloc (hb_len (it)); + + c->push (); + char *data_base = c->head; + + it + | hb_map ([&] (const hb_pair_t &_) + { + FontDict *dict = c->start_embed (); + dict->serialize (c, _.first, opszr, _.second); + return c->head - (const char*)dict; + }) + | hb_sink (sizes) + ; + unsigned data_size = c->head - data_base; + c->pop_pack (false); + + if (unlikely (sizes.in_error ())) return_trace (false); + + /* It just happens that the above is packed right after the header below. + * Such a hack. */ + + /* serialize INDEX header */ + return_trace (CFFIndex::serialize_header (c, hb_iter (sizes), data_size)); + } +}; + +/* FDSelect */ +struct FDSelect0 { + bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const + { + TRACE_SANITIZE (this); + if (unlikely (!(c->check_struct (this)))) + return_trace (false); + if (unlikely (!c->check_array (fds, c->get_num_glyphs ()))) + return_trace (false); + + return_trace (true); + } + + unsigned get_fd (hb_codepoint_t glyph) const + { return fds[glyph]; } + + hb_pair_t get_fd_range (hb_codepoint_t glyph) const + { return {fds[glyph], glyph + 1}; } + + unsigned int get_size (unsigned int num_glyphs) const + { return HBUINT8::static_size * num_glyphs; } + + HBUINT8 fds[HB_VAR_ARRAY]; + + DEFINE_SIZE_MIN (0); +}; + +template +struct FDSelect3_4_Range +{ + bool sanitize (hb_sanitize_context_t *c, const void * /*nullptr*/, unsigned int fdcount) const + { + TRACE_SANITIZE (this); + return_trace (first < c->get_num_glyphs () && (fd < fdcount)); + } + + GID_TYPE first; + FD_TYPE fd; + public: + DEFINE_SIZE_STATIC (GID_TYPE::static_size + FD_TYPE::static_size); +}; + +template +struct FDSelect3_4 +{ + unsigned int get_size () const + { return GID_TYPE::static_size * 2 + ranges.get_size (); } + + bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this) || !ranges.sanitize (c, nullptr, fdcount) || + (nRanges () == 0) || ranges[0].first != 0)) + return_trace (false); + + for (unsigned int i = 1; i < nRanges (); i++) + if (unlikely (ranges[i - 1].first >= ranges[i].first)) + return_trace (false); + + if (unlikely (!sentinel().sanitize (c) || (sentinel() != c->get_num_glyphs ()))) + return_trace (false); + + return_trace (true); + } + + static int _cmp_range (const void *_key, const void *_item) + { + hb_codepoint_t glyph = * (hb_codepoint_t *) _key; + FDSelect3_4_Range *range = (FDSelect3_4_Range *) _item; + + if (glyph < range[0].first) return -1; + if (glyph < range[1].first) return 0; + return +1; + } + + unsigned get_fd (hb_codepoint_t glyph) const + { + auto *range = hb_bsearch (glyph, &ranges[0], nRanges () - 1, sizeof (ranges[0]), _cmp_range); + return range ? range->fd : ranges[nRanges () - 1].fd; + } + + hb_pair_t get_fd_range (hb_codepoint_t glyph) const + { + auto *range = hb_bsearch (glyph, &ranges[0], nRanges () - 1, sizeof (ranges[0]), _cmp_range); + unsigned fd = range ? range->fd : ranges[nRanges () - 1].fd; + hb_codepoint_t end = range ? range[1].first : ranges[nRanges () - 1].first; + return {fd, end}; + } + + GID_TYPE &nRanges () { return ranges.len; } + GID_TYPE nRanges () const { return ranges.len; } + GID_TYPE &sentinel () { return StructAfter (ranges[nRanges () - 1]); } + const GID_TYPE &sentinel () const { return StructAfter (ranges[nRanges () - 1]); } + + ArrayOf, GID_TYPE> ranges; + /* GID_TYPE sentinel */ + + DEFINE_SIZE_ARRAY (GID_TYPE::static_size, ranges); +}; + +typedef FDSelect3_4 FDSelect3; +typedef FDSelect3_4_Range FDSelect3_Range; + +struct FDSelect +{ + bool serialize (hb_serialize_context_t *c, const FDSelect &src, unsigned int num_glyphs) + { + TRACE_SERIALIZE (this); + unsigned int size = src.get_size (num_glyphs); + FDSelect *dest = c->allocate_size (size, false); + if (unlikely (!dest)) return_trace (false); + hb_memcpy (dest, &src, size); + return_trace (true); + } + + unsigned int get_size (unsigned int num_glyphs) const + { + switch (format) + { + case 0: return format.static_size + u.format0.get_size (num_glyphs); + case 3: return format.static_size + u.format3.get_size (); + default:return 0; + } + } + + unsigned get_fd (hb_codepoint_t glyph) const + { + if (this == &Null (FDSelect)) return 0; + + switch (format) + { + case 0: return u.format0.get_fd (glyph); + case 3: return u.format3.get_fd (glyph); + default:return 0; + } + } + /* Returns pair of fd and one after last glyph in range. */ + hb_pair_t get_fd_range (hb_codepoint_t glyph) const + { + if (this == &Null (FDSelect)) return {0, 1}; + + switch (format) + { + case 0: return u.format0.get_fd_range (glyph); + case 3: return u.format3.get_fd_range (glyph); + default:return {0, 1}; + } + } + + bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + + switch (format) + { + case 0: return_trace (u.format0.sanitize (c, fdcount)); + case 3: return_trace (u.format3.sanitize (c, fdcount)); + default:return_trace (false); + } + } + + HBUINT8 format; + union { + FDSelect0 format0; + FDSelect3 format3; + } u; + public: + DEFINE_SIZE_MIN (1); +}; + +template +struct Subrs : CFFIndex +{ + typedef COUNT count_type; + typedef CFFIndex SUPER; +}; + +} /* namespace CFF */ + +#endif /* HB_OT_CFF_COMMON_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-cff1-std-str.hh b/src/3rdparty/harfbuzz-ng/src/hb-ot-cff1-std-str.hh new file mode 100644 index 00000000000..65d56ae18b5 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-cff1-std-str.hh @@ -0,0 +1,425 @@ +/* + * Copyright © 2019 Adobe, 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. + * + * Adobe Author(s): Michiharu Ariza + */ + +#ifndef HB_OT_CFF1_STD_STR_HH +#if 0 /* Make checks happy. */ +#define HB_OT_CFF1_STD_STR_HH +#include "hb.hh" +#endif + +_S(".notdef") +_S("space") +_S("exclam") +_S("quotedbl") +_S("numbersign") +_S("dollar") +_S("percent") +_S("ampersand") +_S("quoteright") +_S("parenleft") +_S("parenright") +_S("asterisk") +_S("plus") +_S("comma") +_S("hyphen") +_S("period") +_S("slash") +_S("zero") +_S("one") +_S("two") +_S("three") +_S("four") +_S("five") +_S("six") +_S("seven") +_S("eight") +_S("nine") +_S("colon") +_S("semicolon") +_S("less") +_S("equal") +_S("greater") +_S("question") +_S("at") +_S("A") +_S("B") +_S("C") +_S("D") +_S("E") +_S("F") +_S("G") +_S("H") +_S("I") +_S("J") +_S("K") +_S("L") +_S("M") +_S("N") +_S("O") +_S("P") +_S("Q") +_S("R") +_S("S") +_S("T") +_S("U") +_S("V") +_S("W") +_S("X") +_S("Y") +_S("Z") +_S("bracketleft") +_S("backslash") +_S("bracketright") +_S("asciicircum") +_S("underscore") +_S("quoteleft") +_S("a") +_S("b") +_S("c") +_S("d") +_S("e") +_S("f") +_S("g") +_S("h") +_S("i") +_S("j") +_S("k") +_S("l") +_S("m") +_S("n") +_S("o") +_S("p") +_S("q") +_S("r") +_S("s") +_S("t") +_S("u") +_S("v") +_S("w") +_S("x") +_S("y") +_S("z") +_S("braceleft") +_S("bar") +_S("braceright") +_S("asciitilde") +_S("exclamdown") +_S("cent") +_S("sterling") +_S("fraction") +_S("yen") +_S("florin") +_S("section") +_S("currency") +_S("quotesingle") +_S("quotedblleft") +_S("guillemotleft") +_S("guilsinglleft") +_S("guilsinglright") +_S("fi") +_S("fl") +_S("endash") +_S("dagger") +_S("daggerdbl") +_S("periodcentered") +_S("paragraph") +_S("bullet") +_S("quotesinglbase") +_S("quotedblbase") +_S("quotedblright") +_S("guillemotright") +_S("ellipsis") +_S("perthousand") +_S("questiondown") +_S("grave") +_S("acute") +_S("circumflex") +_S("tilde") +_S("macron") +_S("breve") +_S("dotaccent") +_S("dieresis") +_S("ring") +_S("cedilla") +_S("hungarumlaut") +_S("ogonek") +_S("caron") +_S("emdash") +_S("AE") +_S("ordfeminine") +_S("Lslash") +_S("Oslash") +_S("OE") +_S("ordmasculine") +_S("ae") +_S("dotlessi") +_S("lslash") +_S("oslash") +_S("oe") +_S("germandbls") +_S("onesuperior") +_S("logicalnot") +_S("mu") +_S("trademark") +_S("Eth") +_S("onehalf") +_S("plusminus") +_S("Thorn") +_S("onequarter") +_S("divide") +_S("brokenbar") +_S("degree") +_S("thorn") +_S("threequarters") +_S("twosuperior") +_S("registered") +_S("minus") +_S("eth") +_S("multiply") +_S("threesuperior") +_S("copyright") +_S("Aacute") +_S("Acircumflex") +_S("Adieresis") +_S("Agrave") +_S("Aring") +_S("Atilde") +_S("Ccedilla") +_S("Eacute") +_S("Ecircumflex") +_S("Edieresis") +_S("Egrave") +_S("Iacute") +_S("Icircumflex") +_S("Idieresis") +_S("Igrave") +_S("Ntilde") +_S("Oacute") +_S("Ocircumflex") +_S("Odieresis") +_S("Ograve") +_S("Otilde") +_S("Scaron") +_S("Uacute") +_S("Ucircumflex") +_S("Udieresis") +_S("Ugrave") +_S("Yacute") +_S("Ydieresis") +_S("Zcaron") +_S("aacute") +_S("acircumflex") +_S("adieresis") +_S("agrave") +_S("aring") +_S("atilde") +_S("ccedilla") +_S("eacute") +_S("ecircumflex") +_S("edieresis") +_S("egrave") +_S("iacute") +_S("icircumflex") +_S("idieresis") +_S("igrave") +_S("ntilde") +_S("oacute") +_S("ocircumflex") +_S("odieresis") +_S("ograve") +_S("otilde") +_S("scaron") +_S("uacute") +_S("ucircumflex") +_S("udieresis") +_S("ugrave") +_S("yacute") +_S("ydieresis") +_S("zcaron") +_S("exclamsmall") +_S("Hungarumlautsmall") +_S("dollaroldstyle") +_S("dollarsuperior") +_S("ampersandsmall") +_S("Acutesmall") +_S("parenleftsuperior") +_S("parenrightsuperior") +_S("twodotenleader") +_S("onedotenleader") +_S("zerooldstyle") +_S("oneoldstyle") +_S("twooldstyle") +_S("threeoldstyle") +_S("fouroldstyle") +_S("fiveoldstyle") +_S("sixoldstyle") +_S("sevenoldstyle") +_S("eightoldstyle") +_S("nineoldstyle") +_S("commasuperior") +_S("threequartersemdash") +_S("periodsuperior") +_S("questionsmall") +_S("asuperior") +_S("bsuperior") +_S("centsuperior") +_S("dsuperior") +_S("esuperior") +_S("isuperior") +_S("lsuperior") +_S("msuperior") +_S("nsuperior") +_S("osuperior") +_S("rsuperior") +_S("ssuperior") +_S("tsuperior") +_S("ff") +_S("ffi") +_S("ffl") +_S("parenleftinferior") +_S("parenrightinferior") +_S("Circumflexsmall") +_S("hyphensuperior") +_S("Gravesmall") +_S("Asmall") +_S("Bsmall") +_S("Csmall") +_S("Dsmall") +_S("Esmall") +_S("Fsmall") +_S("Gsmall") +_S("Hsmall") +_S("Ismall") +_S("Jsmall") +_S("Ksmall") +_S("Lsmall") +_S("Msmall") +_S("Nsmall") +_S("Osmall") +_S("Psmall") +_S("Qsmall") +_S("Rsmall") +_S("Ssmall") +_S("Tsmall") +_S("Usmall") +_S("Vsmall") +_S("Wsmall") +_S("Xsmall") +_S("Ysmall") +_S("Zsmall") +_S("colonmonetary") +_S("onefitted") +_S("rupiah") +_S("Tildesmall") +_S("exclamdownsmall") +_S("centoldstyle") +_S("Lslashsmall") +_S("Scaronsmall") +_S("Zcaronsmall") +_S("Dieresissmall") +_S("Brevesmall") +_S("Caronsmall") +_S("Dotaccentsmall") +_S("Macronsmall") +_S("figuredash") +_S("hypheninferior") +_S("Ogoneksmall") +_S("Ringsmall") +_S("Cedillasmall") +_S("questiondownsmall") +_S("oneeighth") +_S("threeeighths") +_S("fiveeighths") +_S("seveneighths") +_S("onethird") +_S("twothirds") +_S("zerosuperior") +_S("foursuperior") +_S("fivesuperior") +_S("sixsuperior") +_S("sevensuperior") +_S("eightsuperior") +_S("ninesuperior") +_S("zeroinferior") +_S("oneinferior") +_S("twoinferior") +_S("threeinferior") +_S("fourinferior") +_S("fiveinferior") +_S("sixinferior") +_S("seveninferior") +_S("eightinferior") +_S("nineinferior") +_S("centinferior") +_S("dollarinferior") +_S("periodinferior") +_S("commainferior") +_S("Agravesmall") +_S("Aacutesmall") +_S("Acircumflexsmall") +_S("Atildesmall") +_S("Adieresissmall") +_S("Aringsmall") +_S("AEsmall") +_S("Ccedillasmall") +_S("Egravesmall") +_S("Eacutesmall") +_S("Ecircumflexsmall") +_S("Edieresissmall") +_S("Igravesmall") +_S("Iacutesmall") +_S("Icircumflexsmall") +_S("Idieresissmall") +_S("Ethsmall") +_S("Ntildesmall") +_S("Ogravesmall") +_S("Oacutesmall") +_S("Ocircumflexsmall") +_S("Otildesmall") +_S("Odieresissmall") +_S("OEsmall") +_S("Oslashsmall") +_S("Ugravesmall") +_S("Uacutesmall") +_S("Ucircumflexsmall") +_S("Udieresissmall") +_S("Yacutesmall") +_S("Thornsmall") +_S("Ydieresissmall") +_S("001.000") +_S("001.001") +_S("001.002") +_S("001.003") +_S("Black") +_S("Bold") +_S("Book") +_S("Light") +_S("Medium") +_S("Regular") +_S("Roman") +_S("Semibold") + +#endif /* HB_OT_CFF1_STD_STR_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-cff1-table.cc b/src/3rdparty/harfbuzz-ng/src/hb-ot-cff1-table.cc new file mode 100644 index 00000000000..66df28aae1d --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-cff1-table.cc @@ -0,0 +1,620 @@ +/* + * Copyright © 2018 Adobe 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. + * + * Adobe Author(s): Michiharu Ariza + */ + +#include "hb.hh" + +#ifndef HB_NO_CFF + +#include "hb-draw.hh" +#include "hb-algs.hh" +#include "hb-ot-cff1-table.hh" +#include "hb-cff1-interp-cs.hh" + +using namespace CFF; + +struct sid_to_gid_t +{ + uint16_t sid; + uint8_t gid; + + int cmp (uint16_t a) const + { + if (a == sid) return 0; + return (a < sid) ? -1 : 1; + } +}; + +/* SID to code */ +static const uint8_t standard_encoding_to_code [] = +{ + 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, + 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 177, + 178, 179, 180, 182, 183, 184, 185, 186, 187, 188, 189, 191, 193, 194, 195, 196, + 197, 198, 199, 200, 202, 203, 205, 206, 207, 208, 225, 227, 232, 233, 234, 235, + 241, 245, 248, 249, 250, 251 +}; + +/* SID to code */ +static const uint8_t expert_encoding_to_code [] = +{ + 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 45, 46, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 59, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 87, 88, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 189, 0, 0, 188, 0, + 0, 0, 0, 190, 202, 0, 0, 0, 0, 203, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 33, 34, 36, 37, 38, 39, 40, 41, 42, 43, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, 60, 61, 62, 63, 65, 66, 67, + 68, 69, 73, 76, 77, 78, 79, 82, 83, 84, 86, 89, 90, 91, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 161, 162, 163, 166, 167, 168, 169, 170, 172, 175, 178, 179, 182, 183, 184, 191, + 192, 193, 194, 195, 196, 197, 200, 204, 205, 206, 207, 208, 209, 210, 211, 212, + 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, + 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 +}; + +/* glyph ID to SID */ +static const uint16_t expert_charset_to_sid [] = +{ + 0, 1, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 13, 14, 15, 99, + 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 27, 28, 249, 250, 251, 252, + 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 109, 110, + 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, + 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, + 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, 318, 158, 155, 163, 319, 320, 321, 322, 323, 324, 325, 326, 150, + 164, 169, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, + 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, + 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, + 373, 374, 375, 376, 377, 378 +}; + +/* glyph ID to SID */ +static const uint16_t expert_subset_charset_to_sid [] = +{ + 0, 1, 231, 232, 235, 236, 237, 238, 13, 14, 15, 99, 239, 240, 241, 242, + 243, 244, 245, 246, 247, 248, 27, 28, 249, 250, 251, 253, 254, 255, 256, 257, + 258, 259, 260, 261, 262, 263, 264, 265, 266, 109, 110, 267, 268, 269, 270, 272, + 300, 301, 302, 305, 314, 315, 158, 155, 163, 320, 321, 322, 323, 324, 325, 326, + 150, 164, 169, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, + 340, 341, 342, 343, 344, 345, 346 +}; + +/* SID to glyph ID */ +static const sid_to_gid_t expert_charset_sid_to_gid [] = +{ + { 1, 1 }, { 13, 12 }, { 14, 13 }, { 15, 14 }, + { 27, 26 }, { 28, 27 }, { 99, 15 }, { 109, 46 }, + { 110, 47 }, { 150, 111 }, { 155, 101 }, { 158, 100 }, + { 163, 102 }, { 164, 112 }, { 169, 113 }, { 229, 2 }, + { 230, 3 }, { 231, 4 }, { 232, 5 }, { 233, 6 }, + { 234, 7 }, { 235, 8 }, { 236, 9 }, { 237, 10 }, + { 238, 11 }, { 239, 16 }, { 240, 17 }, { 241, 18 }, + { 242, 19 }, { 243, 20 }, { 244, 21 }, { 245, 22 }, + { 246, 23 }, { 247, 24 }, { 248, 25 }, { 249, 28 }, + { 250, 29 }, { 251, 30 }, { 252, 31 }, { 253, 32 }, + { 254, 33 }, { 255, 34 }, { 256, 35 }, { 257, 36 }, + { 258, 37 }, { 259, 38 }, { 260, 39 }, { 261, 40 }, + { 262, 41 }, { 263, 42 }, { 264, 43 }, { 265, 44 }, + { 266, 45 }, { 267, 48 }, { 268, 49 }, { 269, 50 }, + { 270, 51 }, { 271, 52 }, { 272, 53 }, { 273, 54 }, + { 274, 55 }, { 275, 56 }, { 276, 57 }, { 277, 58 }, + { 278, 59 }, { 279, 60 }, { 280, 61 }, { 281, 62 }, + { 282, 63 }, { 283, 64 }, { 284, 65 }, { 285, 66 }, + { 286, 67 }, { 287, 68 }, { 288, 69 }, { 289, 70 }, + { 290, 71 }, { 291, 72 }, { 292, 73 }, { 293, 74 }, + { 294, 75 }, { 295, 76 }, { 296, 77 }, { 297, 78 }, + { 298, 79 }, { 299, 80 }, { 300, 81 }, { 301, 82 }, + { 302, 83 }, { 303, 84 }, { 304, 85 }, { 305, 86 }, + { 306, 87 }, { 307, 88 }, { 308, 89 }, { 309, 90 }, + { 310, 91 }, { 311, 92 }, { 312, 93 }, { 313, 94 }, + { 314, 95 }, { 315, 96 }, { 316, 97 }, { 317, 98 }, + { 318, 99 }, { 319, 103 }, { 320, 104 }, { 321, 105 }, + { 322, 106 }, { 323, 107 }, { 324, 108 }, { 325, 109 }, + { 326, 110 }, { 327, 114 }, { 328, 115 }, { 329, 116 }, + { 330, 117 }, { 331, 118 }, { 332, 119 }, { 333, 120 }, + { 334, 121 }, { 335, 122 }, { 336, 123 }, { 337, 124 }, + { 338, 125 }, { 339, 126 }, { 340, 127 }, { 341, 128 }, + { 342, 129 }, { 343, 130 }, { 344, 131 }, { 345, 132 }, + { 346, 133 }, { 347, 134 }, { 348, 135 }, { 349, 136 }, + { 350, 137 }, { 351, 138 }, { 352, 139 }, { 353, 140 }, + { 354, 141 }, { 355, 142 }, { 356, 143 }, { 357, 144 }, + { 358, 145 }, { 359, 146 }, { 360, 147 }, { 361, 148 }, + { 362, 149 }, { 363, 150 }, { 364, 151 }, { 365, 152 }, + { 366, 153 }, { 367, 154 }, { 368, 155 }, { 369, 156 }, + { 370, 157 }, { 371, 158 }, { 372, 159 }, { 373, 160 }, + { 374, 161 }, { 375, 162 }, { 376, 163 }, { 377, 164 }, + { 378, 165 } +}; + +/* SID to glyph ID */ +static const sid_to_gid_t expert_subset_charset_sid_to_gid [] = +{ + { 1, 1 }, { 13, 8 }, { 14, 9 }, { 15, 10 }, + { 27, 22 }, { 28, 23 }, { 99, 11 }, { 109, 41 }, + { 110, 42 }, { 150, 64 }, { 155, 55 }, { 158, 54 }, + { 163, 56 }, { 164, 65 }, { 169, 66 }, { 231, 2 }, + { 232, 3 }, { 235, 4 }, { 236, 5 }, { 237, 6 }, + { 238, 7 }, { 239, 12 }, { 240, 13 }, { 241, 14 }, + { 242, 15 }, { 243, 16 }, { 244, 17 }, { 245, 18 }, + { 246, 19 }, { 247, 20 }, { 248, 21 }, { 249, 24 }, + { 250, 25 }, { 251, 26 }, { 253, 27 }, { 254, 28 }, + { 255, 29 }, { 256, 30 }, { 257, 31 }, { 258, 32 }, + { 259, 33 }, { 260, 34 }, { 261, 35 }, { 262, 36 }, + { 263, 37 }, { 264, 38 }, { 265, 39 }, { 266, 40 }, + { 267, 43 }, { 268, 44 }, { 269, 45 }, { 270, 46 }, + { 272, 47 }, { 300, 48 }, { 301, 49 }, { 302, 50 }, + { 305, 51 }, { 314, 52 }, { 315, 53 }, { 320, 57 }, + { 321, 58 }, { 322, 59 }, { 323, 60 }, { 324, 61 }, + { 325, 62 }, { 326, 63 }, { 327, 67 }, { 328, 68 }, + { 329, 69 }, { 330, 70 }, { 331, 71 }, { 332, 72 }, + { 333, 73 }, { 334, 74 }, { 335, 75 }, { 336, 76 }, + { 337, 77 }, { 338, 78 }, { 339, 79 }, { 340, 80 }, + { 341, 81 }, { 342, 82 }, { 343, 83 }, { 344, 84 }, + { 345, 85 }, { 346, 86 } +}; + +/* code to SID */ +static const uint8_t standard_encoding_to_sid [] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, + 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 0, 111, 112, 113, 114, 0, 115, 116, 117, 118, 119, 120, 121, 122, 0, 123, + 0, 124, 125, 126, 127, 128, 129, 130, 131, 0, 132, 133, 0, 134, 135, 136, + 137, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 138, 0, 139, 0, 0, 0, 0, 140, 141, 142, 143, 0, 0, 0, 0, + 0, 144, 0, 0, 0, 145, 0, 0, 146, 147, 148, 149, 0, 0, 0, 0 +}; + +hb_codepoint_t OT::cff1::lookup_standard_encoding_for_code (hb_codepoint_t sid) +{ + if (sid < ARRAY_LENGTH (standard_encoding_to_code)) + return (hb_codepoint_t)standard_encoding_to_code[sid]; + else + return 0; +} + +hb_codepoint_t OT::cff1::lookup_expert_encoding_for_code (hb_codepoint_t sid) +{ + if (sid < ARRAY_LENGTH (expert_encoding_to_code)) + return (hb_codepoint_t)expert_encoding_to_code[sid]; + else + return 0; +} + +hb_codepoint_t OT::cff1::lookup_expert_charset_for_sid (hb_codepoint_t glyph) +{ + if (glyph < ARRAY_LENGTH (expert_charset_to_sid)) + return (hb_codepoint_t)expert_charset_to_sid[glyph]; + else + return 0; +} + +hb_codepoint_t OT::cff1::lookup_expert_subset_charset_for_sid (hb_codepoint_t glyph) +{ + if (glyph < ARRAY_LENGTH (expert_subset_charset_to_sid)) + return (hb_codepoint_t)expert_subset_charset_to_sid[glyph]; + else + return 0; +} + +hb_codepoint_t OT::cff1::lookup_expert_charset_for_glyph (hb_codepoint_t sid) +{ + const auto *pair = hb_sorted_array (expert_charset_sid_to_gid).bsearch (sid); + return pair ? pair->gid : 0; +} + +hb_codepoint_t OT::cff1::lookup_expert_subset_charset_for_glyph (hb_codepoint_t sid) +{ + const auto *pair = hb_sorted_array (expert_subset_charset_sid_to_gid).bsearch (sid); + return pair ? pair->gid : 0; +} + +hb_codepoint_t OT::cff1::lookup_standard_encoding_for_sid (hb_codepoint_t code) +{ + if (code < ARRAY_LENGTH (standard_encoding_to_sid)) + return (hb_codepoint_t)standard_encoding_to_sid[code]; + else + return CFF_UNDEF_SID; +} + +struct bounds_t +{ + void init () + { + min.set_int (INT_MAX, INT_MAX); + max.set_int (INT_MIN, INT_MIN); + } + + void update (const point_t &pt) + { + if (pt.x < min.x) min.x = pt.x; + if (pt.x > max.x) max.x = pt.x; + if (pt.y < min.y) min.y = pt.y; + if (pt.y > max.y) max.y = pt.y; + } + + void merge (const bounds_t &b) + { + if (empty ()) + *this = b; + else if (!b.empty ()) + { + if (b.min.x < min.x) min.x = b.min.x; + if (b.max.x > max.x) max.x = b.max.x; + if (b.min.y < min.y) min.y = b.min.y; + if (b.max.y > max.y) max.y = b.max.y; + } + } + + void offset (const point_t &delta) + { + if (!empty ()) + { + min.move (delta); + max.move (delta); + } + } + + bool empty () const { return (min.x >= max.x) || (min.y >= max.y); } + + point_t min; + point_t max; +}; + +struct cff1_extents_param_t +{ + cff1_extents_param_t (const OT::cff1::accelerator_t *_cff) : cff (_cff) + { + bounds.init (); + } + + void start_path () { path_open = true; } + void end_path () { path_open = false; } + bool is_path_open () const { return path_open; } + + bool path_open = false; + bounds_t bounds; + + const OT::cff1::accelerator_t *cff; +}; + +struct cff1_path_procs_extents_t : path_procs_t +{ + static void moveto (cff1_cs_interp_env_t &env, cff1_extents_param_t& param, const point_t &pt) + { + param.end_path (); + env.moveto (pt); + } + + static void line (cff1_cs_interp_env_t &env, cff1_extents_param_t& param, const point_t &pt1) + { + if (!param.is_path_open ()) + { + param.start_path (); + param.bounds.update (env.get_pt ()); + } + env.moveto (pt1); + param.bounds.update (env.get_pt ()); + } + + static void curve (cff1_cs_interp_env_t &env, cff1_extents_param_t& param, const point_t &pt1, const point_t &pt2, const point_t &pt3) + { + if (!param.is_path_open ()) + { + param.start_path (); + param.bounds.update (env.get_pt ()); + } + /* include control points */ + param.bounds.update (pt1); + param.bounds.update (pt2); + env.moveto (pt3); + param.bounds.update (env.get_pt ()); + } +}; + +static bool _get_bounds (const OT::cff1::accelerator_t *cff, hb_codepoint_t glyph, bounds_t &bounds, bool in_seac=false); + +struct cff1_cs_opset_extents_t : cff1_cs_opset_t +{ + static void process_seac (cff1_cs_interp_env_t &env, cff1_extents_param_t& param) + { + unsigned int n = env.argStack.get_count (); + point_t delta; + delta.x = env.argStack[n-4]; + delta.y = env.argStack[n-3]; + hb_codepoint_t base = param.cff->std_code_to_glyph (env.argStack[n-2].to_int ()); + hb_codepoint_t accent = param.cff->std_code_to_glyph (env.argStack[n-1].to_int ()); + + bounds_t base_bounds, accent_bounds; + if (likely (!env.in_seac && base && accent + && _get_bounds (param.cff, base, base_bounds, true) + && _get_bounds (param.cff, accent, accent_bounds, true))) + { + param.bounds.merge (base_bounds); + accent_bounds.offset (delta); + param.bounds.merge (accent_bounds); + } + else + env.set_error (); + } +}; + +bool _get_bounds (const OT::cff1::accelerator_t *cff, hb_codepoint_t glyph, bounds_t &bounds, bool in_seac) +{ + bounds.init (); + if (unlikely (!cff->is_valid () || (glyph >= cff->num_glyphs))) return false; + + unsigned int fd = cff->fdSelect->get_fd (glyph); + const hb_ubytes_t str = (*cff->charStrings)[glyph]; + cff1_cs_interp_env_t env (str, *cff, fd); + env.set_in_seac (in_seac); + cff1_cs_interpreter_t interp (env); + cff1_extents_param_t param (cff); + if (unlikely (!interp.interpret (param))) return false; + bounds = param.bounds; + return true; +} + +bool OT::cff1::accelerator_t::get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const +{ +#ifdef HB_NO_OT_FONT_CFF + /* XXX Remove check when this code moves to .hh file. */ + return true; +#endif + + bounds_t bounds; + + if (!_get_bounds (this, glyph, bounds)) + return false; + + if (bounds.min.x >= bounds.max.x) + { + extents->width = 0; + extents->x_bearing = 0; + } + else + { + extents->x_bearing = roundf (bounds.min.x.to_real ()); + extents->width = roundf (bounds.max.x.to_real () - extents->x_bearing); + } + if (bounds.min.y >= bounds.max.y) + { + extents->height = 0; + extents->y_bearing = 0; + } + else + { + extents->y_bearing = roundf (bounds.max.y.to_real ()); + extents->height = roundf (bounds.min.y.to_real () - extents->y_bearing); + } + + font->scale_glyph_extents (extents); + + return true; +} + +struct cff1_path_param_t +{ + cff1_path_param_t (const OT::cff1::accelerator_t *cff_, hb_font_t *font_, + hb_draw_session_t &draw_session_, point_t *delta_) + { + draw_session = &draw_session_; + cff = cff_; + font = font_; + delta = delta_; + } + + void move_to (const point_t &p) + { + point_t point = p; + if (delta) point.move (*delta); + draw_session->move_to (font->em_fscalef_x (point.x.to_real ()), font->em_fscalef_y (point.y.to_real ())); + } + + void line_to (const point_t &p) + { + point_t point = p; + if (delta) point.move (*delta); + draw_session->line_to (font->em_fscalef_x (point.x.to_real ()), font->em_fscalef_y (point.y.to_real ())); + } + + void cubic_to (const point_t &p1, const point_t &p2, const point_t &p3) + { + point_t point1 = p1, point2 = p2, point3 = p3; + if (delta) + { + point1.move (*delta); + point2.move (*delta); + point3.move (*delta); + } + draw_session->cubic_to (font->em_fscalef_x (point1.x.to_real ()), font->em_fscalef_y (point1.y.to_real ()), + font->em_fscalef_x (point2.x.to_real ()), font->em_fscalef_y (point2.y.to_real ()), + font->em_fscalef_x (point3.x.to_real ()), font->em_fscalef_y (point3.y.to_real ())); + } + + void end_path () { draw_session->close_path (); } + + hb_font_t *font; + hb_draw_session_t *draw_session; + point_t *delta; + + const OT::cff1::accelerator_t *cff; +}; + +struct cff1_path_procs_path_t : path_procs_t +{ + static void moveto (cff1_cs_interp_env_t &env, cff1_path_param_t& param, const point_t &pt) + { + param.move_to (pt); + env.moveto (pt); + } + + static void line (cff1_cs_interp_env_t &env, cff1_path_param_t ¶m, const point_t &pt1) + { + param.line_to (pt1); + env.moveto (pt1); + } + + static void curve (cff1_cs_interp_env_t &env, cff1_path_param_t ¶m, const point_t &pt1, const point_t &pt2, const point_t &pt3) + { + param.cubic_to (pt1, pt2, pt3); + env.moveto (pt3); + } +}; + +static bool _get_path (const OT::cff1::accelerator_t *cff, hb_font_t *font, hb_codepoint_t glyph, + hb_draw_session_t &draw_session, bool in_seac = false, point_t *delta = nullptr); + +struct cff1_cs_opset_path_t : cff1_cs_opset_t +{ + static void process_seac (cff1_cs_interp_env_t &env, cff1_path_param_t& param) + { + /* End previous path */ + param.end_path (); + + unsigned int n = env.argStack.get_count (); + point_t delta; + delta.x = env.argStack[n-4]; + delta.y = env.argStack[n-3]; + hb_codepoint_t base = param.cff->std_code_to_glyph (env.argStack[n-2].to_int ()); + hb_codepoint_t accent = param.cff->std_code_to_glyph (env.argStack[n-1].to_int ()); + + if (unlikely (!(!env.in_seac && base && accent + && _get_path (param.cff, param.font, base, *param.draw_session, true) + && _get_path (param.cff, param.font, accent, *param.draw_session, true, &delta)))) + env.set_error (); + } +}; + +bool _get_path (const OT::cff1::accelerator_t *cff, hb_font_t *font, hb_codepoint_t glyph, + hb_draw_session_t &draw_session, bool in_seac, point_t *delta) +{ + if (unlikely (!cff->is_valid () || (glyph >= cff->num_glyphs))) return false; + + unsigned int fd = cff->fdSelect->get_fd (glyph); + const hb_ubytes_t str = (*cff->charStrings)[glyph]; + cff1_cs_interp_env_t env (str, *cff, fd); + env.set_in_seac (in_seac); + cff1_cs_interpreter_t interp (env); + cff1_path_param_t param (cff, font, draw_session, delta); + if (unlikely (!interp.interpret (param))) return false; + + /* Let's end the path specially since it is called inside seac also */ + param.end_path (); + + return true; +} + +bool OT::cff1::accelerator_t::paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const +{ + funcs->push_clip_glyph (data, glyph, font); + funcs->color (data, true, foreground); + funcs->pop_clip (data); + + return true; +} + +bool OT::cff1::accelerator_t::get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const +{ +#ifdef HB_NO_OT_FONT_CFF + /* XXX Remove check when this code moves to .hh file. */ + return true; +#endif + + return _get_path (this, font, glyph, draw_session); +} + +struct get_seac_param_t +{ + get_seac_param_t (const OT::cff1::accelerator_subset_t *_cff) : cff (_cff) {} + + bool has_seac () const { return base && accent; } + + const OT::cff1::accelerator_subset_t *cff; + hb_codepoint_t base = 0; + hb_codepoint_t accent = 0; +}; + +struct cff1_cs_opset_seac_t : cff1_cs_opset_t +{ + static void process_seac (cff1_cs_interp_env_t &env, get_seac_param_t& param) + { + unsigned int n = env.argStack.get_count (); + hb_codepoint_t base_char = (hb_codepoint_t)env.argStack[n-2].to_int (); + hb_codepoint_t accent_char = (hb_codepoint_t)env.argStack[n-1].to_int (); + + param.base = param.cff->std_code_to_glyph (base_char); + param.accent = param.cff->std_code_to_glyph (accent_char); + } +}; + +bool OT::cff1::accelerator_subset_t::get_seac_components (hb_codepoint_t glyph, hb_codepoint_t *base, hb_codepoint_t *accent) const +{ + if (unlikely (!is_valid () || (glyph >= num_glyphs))) return false; + + unsigned int fd = fdSelect->get_fd (glyph); + const hb_ubytes_t str = (*charStrings)[glyph]; + cff1_cs_interp_env_t env (str, *this, fd); + cff1_cs_interpreter_t interp (env); + get_seac_param_t param (this); + if (unlikely (!interp.interpret (param))) return false; + + if (param.has_seac ()) + { + *base = param.base; + *accent = param.accent; + return true; + } + return false; +} + + +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-cff1-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-ot-cff1-table.hh new file mode 100644 index 00000000000..1e81dcb5e3a --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-cff1-table.hh @@ -0,0 +1,1544 @@ +/* + * Copyright © 2018 Adobe 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. + * + * Adobe Author(s): Michiharu Ariza + */ + +#ifndef HB_OT_CFF1_TABLE_HH +#define HB_OT_CFF1_TABLE_HH + +#include "hb-ot-cff-common.hh" +#include "hb-subset-cff-common.hh" +#include "hb-draw.hh" +#include "hb-paint.hh" + +#define HB_STRING_ARRAY_NAME cff1_std_strings +#define HB_STRING_ARRAY_LIST "hb-ot-cff1-std-str.hh" +#include "hb-string-array.hh" +#undef HB_STRING_ARRAY_LIST +#undef HB_STRING_ARRAY_NAME + +namespace CFF { + +/* + * CFF -- Compact Font Format (CFF) + * https://www.adobe.com/content/dam/acom/en/devnet/font/pdfs/5176.CFF.pdf + */ +#define HB_OT_TAG_CFF1 HB_TAG('C','F','F',' ') + +#define CFF_UNDEF_SID CFF_UNDEF_CODE + +enum EncodingID { StandardEncoding = 0, ExpertEncoding = 1 }; +enum CharsetID { ISOAdobeCharset = 0, ExpertCharset = 1, ExpertSubsetCharset = 2 }; + +typedef CFFIndex CFF1Index; + +typedef CFFIndex CFF1Index; +typedef CFF1Index CFF1CharStrings; +typedef Subrs CFF1Subrs; + +struct CFF1FDSelect : FDSelect {}; + +/* Encoding */ +struct Encoding0 { + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (codes.sanitize (c)); + } + + hb_codepoint_t get_code (hb_codepoint_t glyph) const + { + assert (glyph > 0); + glyph--; + if (glyph < nCodes ()) + { + return (hb_codepoint_t)codes[glyph]; + } + else + return CFF_UNDEF_CODE; + } + + HBUINT8 &nCodes () { return codes.len; } + HBUINT8 nCodes () const { return codes.len; } + + ArrayOf codes; + + DEFINE_SIZE_ARRAY_SIZED (1, codes); +}; + +struct Encoding1_Range { + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + HBUINT8 first; + HBUINT8 nLeft; + + DEFINE_SIZE_STATIC (2); +}; + +struct Encoding1 { + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (ranges.sanitize (c)); + } + + hb_codepoint_t get_code (hb_codepoint_t glyph) const + { + /* TODO: Add cache like get_sid. */ + assert (glyph > 0); + glyph--; + for (unsigned int i = 0; i < nRanges (); i++) + { + if (glyph <= ranges[i].nLeft) + { + hb_codepoint_t code = (hb_codepoint_t) ranges[i].first + glyph; + return (likely (code < 0x100) ? code: CFF_UNDEF_CODE); + } + glyph -= (ranges[i].nLeft + 1); + } + return CFF_UNDEF_CODE; + } + + HBUINT8 &nRanges () { return ranges.len; } + HBUINT8 nRanges () const { return ranges.len; } + + ArrayOf ranges; + + DEFINE_SIZE_ARRAY_SIZED (1, ranges); +}; + +struct SuppEncoding { + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + HBUINT8 code; + HBUINT16 glyph; + + DEFINE_SIZE_STATIC (3); +}; + +struct CFF1SuppEncData { + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (supps.sanitize (c)); + } + + void get_codes (hb_codepoint_t sid, hb_vector_t &codes) const + { + for (unsigned int i = 0; i < nSups (); i++) + if (sid == supps[i].glyph) + codes.push (supps[i].code); + } + + HBUINT8 &nSups () { return supps.len; } + HBUINT8 nSups () const { return supps.len; } + + ArrayOf supps; + + DEFINE_SIZE_ARRAY_SIZED (1, supps); +}; + +struct Encoding +{ + /* serialize a fullset Encoding */ + bool serialize (hb_serialize_context_t *c, const Encoding &src) + { + TRACE_SERIALIZE (this); + return_trace (c->embed (src)); + } + + /* serialize a subset Encoding */ + bool serialize (hb_serialize_context_t *c, + uint8_t format, + unsigned int enc_count, + const hb_vector_t& code_ranges, + const hb_vector_t& supp_codes) + { + TRACE_SERIALIZE (this); + Encoding *dest = c->extend_min (this); + if (unlikely (!dest)) return_trace (false); + dest->format = format | ((supp_codes.length > 0) ? 0x80 : 0); + switch (format) { + case 0: + { + Encoding0 *fmt0 = c->allocate_size (Encoding0::min_size + HBUINT8::static_size * enc_count); + if (unlikely (!fmt0)) return_trace (false); + fmt0->nCodes () = enc_count; + unsigned int glyph = 0; + for (unsigned int i = 0; i < code_ranges.length; i++) + { + hb_codepoint_t code = code_ranges[i].code; + for (int left = (int)code_ranges[i].glyph; left >= 0; left--) + fmt0->codes[glyph++] = code++; + if (unlikely (!((glyph <= 0x100) && (code <= 0x100)))) + return_trace (false); + } + } + break; + + case 1: + { + Encoding1 *fmt1 = c->allocate_size (Encoding1::min_size + Encoding1_Range::static_size * code_ranges.length); + if (unlikely (!fmt1)) return_trace (false); + fmt1->nRanges () = code_ranges.length; + for (unsigned int i = 0; i < code_ranges.length; i++) + { + if (unlikely (!((code_ranges[i].code <= 0xFF) && (code_ranges[i].glyph <= 0xFF)))) + return_trace (false); + fmt1->ranges[i].first = code_ranges[i].code; + fmt1->ranges[i].nLeft = code_ranges[i].glyph; + } + } + break; + + } + + if (supp_codes.length) + { + CFF1SuppEncData *suppData = c->allocate_size (CFF1SuppEncData::min_size + SuppEncoding::static_size * supp_codes.length); + if (unlikely (!suppData)) return_trace (false); + suppData->nSups () = supp_codes.length; + for (unsigned int i = 0; i < supp_codes.length; i++) + { + suppData->supps[i].code = supp_codes[i].code; + suppData->supps[i].glyph = supp_codes[i].glyph; /* actually SID */ + } + } + + return_trace (true); + } + + unsigned int get_size () const + { + unsigned int size = min_size; + switch (table_format ()) + { + case 0: size += u.format0.get_size (); break; + case 1: size += u.format1.get_size (); break; + } + if (has_supplement ()) + size += suppEncData ().get_size (); + return size; + } + + hb_codepoint_t get_code (hb_codepoint_t glyph) const + { + switch (table_format ()) + { + case 0: return u.format0.get_code (glyph); + case 1: return u.format1.get_code (glyph); + default:return 0; + } + } + + uint8_t table_format () const { return format & 0x7F; } + bool has_supplement () const { return format & 0x80; } + + void get_supplement_codes (hb_codepoint_t sid, hb_vector_t &codes) const + { + codes.resize (0); + if (has_supplement ()) + suppEncData().get_codes (sid, codes); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + + switch (table_format ()) + { + case 0: if (unlikely (!u.format0.sanitize (c))) { return_trace (false); } break; + case 1: if (unlikely (!u.format1.sanitize (c))) { return_trace (false); } break; + default:return_trace (false); + } + return_trace (likely (!has_supplement () || suppEncData ().sanitize (c))); + } + + protected: + const CFF1SuppEncData &suppEncData () const + { + switch (table_format ()) + { + case 0: return StructAfter (u.format0.codes[u.format0.nCodes ()-1]); + case 1: return StructAfter (u.format1.ranges[u.format1.nRanges ()-1]); + default:return Null (CFF1SuppEncData); + } + } + + public: + HBUINT8 format; + union { + Encoding0 format0; + Encoding1 format1; + } u; + /* CFF1SuppEncData suppEncData; */ + + DEFINE_SIZE_MIN (1); +}; + +/* Charset */ +struct Charset0 +{ + bool sanitize (hb_sanitize_context_t *c, unsigned int num_glyphs, unsigned *num_charset_entries) const + { + TRACE_SANITIZE (this); + if (num_charset_entries) *num_charset_entries = num_glyphs; + return_trace (sids.sanitize (c, num_glyphs - 1)); + } + + hb_codepoint_t get_sid (hb_codepoint_t glyph, unsigned num_glyphs) const + { + if (unlikely (glyph >= num_glyphs)) return 0; + if (unlikely (glyph == 0)) + return 0; + else + return sids[glyph - 1]; + } + + void collect_glyph_to_sid_map (glyph_to_sid_map_t *mapping, unsigned int num_glyphs) const + { + mapping->resize (num_glyphs, false); + for (hb_codepoint_t gid = 1; gid < num_glyphs; gid++) + mapping->arrayZ[gid] = {sids[gid - 1], gid}; + } + + hb_codepoint_t get_glyph (hb_codepoint_t sid, unsigned int num_glyphs) const + { + if (sid == 0) + return 0; + + for (unsigned int glyph = 1; glyph < num_glyphs; glyph++) + { + if (sids[glyph-1] == sid) + return glyph; + } + return 0; + } + + static unsigned int get_size (unsigned int num_glyphs) + { + assert (num_glyphs > 0); + return UnsizedArrayOf::get_size (num_glyphs - 1); + } + + UnsizedArrayOf sids; + + DEFINE_SIZE_ARRAY(0, sids); +}; + +template +struct Charset_Range { + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + HBUINT16 first; + TYPE nLeft; + + DEFINE_SIZE_STATIC (HBUINT16::static_size + TYPE::static_size); +}; + +template +struct Charset1_2 { + bool sanitize (hb_sanitize_context_t *c, unsigned int num_glyphs, unsigned *num_charset_entries) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + num_glyphs--; + unsigned i; + for (i = 0; num_glyphs > 0; i++) + { + if (unlikely (!ranges[i].sanitize (c) || (num_glyphs < ranges[i].nLeft + 1))) + return_trace (false); + num_glyphs -= (ranges[i].nLeft + 1); + } + if (num_charset_entries) + *num_charset_entries = i; + return_trace (true); + } + + hb_codepoint_t get_sid (hb_codepoint_t glyph, unsigned num_glyphs, + code_pair_t *cache = nullptr) const + { + if (unlikely (glyph >= num_glyphs)) return 0; + unsigned i; + hb_codepoint_t start_glyph; + if (cache && likely (cache->glyph <= glyph)) + { + i = cache->code; + start_glyph = cache->glyph; + } + else + { + if (unlikely (glyph == 0)) return 0; + i = 0; + start_glyph = 1; + } + glyph -= start_glyph; + for (;; i++) + { + unsigned count = ranges[i].nLeft; + if (glyph <= count) + { + if (cache) + *cache = {i, start_glyph}; + return ranges[i].first + glyph; + } + count++; + start_glyph += count; + glyph -= count; + } + + return 0; + } + + void collect_glyph_to_sid_map (glyph_to_sid_map_t *mapping, unsigned int num_glyphs) const + { + mapping->resize (num_glyphs, false); + hb_codepoint_t gid = 1; + if (gid >= num_glyphs) + return; + for (unsigned i = 0;; i++) + { + hb_codepoint_t sid = ranges[i].first; + unsigned count = ranges[i].nLeft + 1; + unsigned last = gid + count; + for (unsigned j = 0; j < count; j++) + mapping->arrayZ[gid++] = {sid++, last - 1}; + + if (gid >= num_glyphs) + break; + } + } + + hb_codepoint_t get_glyph (hb_codepoint_t sid, unsigned int num_glyphs) const + { + if (sid == 0) return 0; + hb_codepoint_t glyph = 1; + for (unsigned int i = 0;; i++) + { + if (glyph >= num_glyphs) + return 0; + if ((ranges[i].first <= sid) && (sid <= ranges[i].first + ranges[i].nLeft)) + return glyph + (sid - ranges[i].first); + glyph += (ranges[i].nLeft + 1); + } + + return 0; + } + + unsigned int get_size (unsigned int num_glyphs) const + { + int glyph = (int) num_glyphs; + unsigned num_ranges = 0; + + assert (glyph > 0); + glyph--; + for (unsigned int i = 0; glyph > 0; i++) + { + glyph -= (ranges[i].nLeft + 1); + num_ranges++; + } + + return get_size_for_ranges (num_ranges); + } + + static unsigned int get_size_for_ranges (unsigned int num_ranges) + { + return UnsizedArrayOf >::get_size (num_ranges); + } + + UnsizedArrayOf> ranges; + + DEFINE_SIZE_ARRAY (0, ranges); +}; + +typedef Charset1_2 Charset1; +typedef Charset1_2 Charset2; +typedef Charset_Range Charset1_Range; +typedef Charset_Range Charset2_Range; + +struct Charset +{ + /* serialize a fullset Charset */ + bool serialize (hb_serialize_context_t *c, const Charset &src, unsigned int num_glyphs) + { + TRACE_SERIALIZE (this); + return_trace (c->embed ((const char *) &src, src.get_size (num_glyphs))); + } + + /* serialize a subset Charset */ + bool serialize (hb_serialize_context_t *c, + uint8_t format, + unsigned int num_glyphs, + const hb_vector_t& sid_ranges) + { + TRACE_SERIALIZE (this); + Charset *dest = c->extend_min (this); + if (unlikely (!dest)) return_trace (false); + dest->format = format; + switch (format) + { + case 0: + { + Charset0 *fmt0 = c->allocate_size (Charset0::get_size (num_glyphs), false); + if (unlikely (!fmt0)) return_trace (false); + unsigned int glyph = 0; + for (unsigned int i = 0; i < sid_ranges.length; i++) + { + hb_codepoint_t sid = sid_ranges.arrayZ[i].code; + for (int left = (int)sid_ranges.arrayZ[i].glyph; left >= 0; left--) + fmt0->sids[glyph++] = sid++; + } + } + break; + + case 1: + { + Charset1 *fmt1 = c->allocate_size (Charset1::get_size_for_ranges (sid_ranges.length), false); + if (unlikely (!fmt1)) return_trace (false); + hb_codepoint_t all_glyphs = 0; + for (unsigned int i = 0; i < sid_ranges.length; i++) + { + auto &_ = sid_ranges.arrayZ[i]; + all_glyphs |= _.glyph; + fmt1->ranges[i].first = _.code; + fmt1->ranges[i].nLeft = _.glyph; + } + if (unlikely (!(all_glyphs <= 0xFF))) + return_trace (false); + } + break; + + case 2: + { + Charset2 *fmt2 = c->allocate_size (Charset2::get_size_for_ranges (sid_ranges.length), false); + if (unlikely (!fmt2)) return_trace (false); + hb_codepoint_t all_glyphs = 0; + for (unsigned int i = 0; i < sid_ranges.length; i++) + { + auto &_ = sid_ranges.arrayZ[i]; + all_glyphs |= _.glyph; + fmt2->ranges[i].first = _.code; + fmt2->ranges[i].nLeft = _.glyph; + } + if (unlikely (!(all_glyphs <= 0xFFFF))) + return_trace (false); + } + break; + + } + return_trace (true); + } + + unsigned int get_size (unsigned int num_glyphs) const + { + switch (format) + { + case 0: return min_size + u.format0.get_size (num_glyphs); + case 1: return min_size + u.format1.get_size (num_glyphs); + case 2: return min_size + u.format2.get_size (num_glyphs); + default:return 0; + } + } + + hb_codepoint_t get_sid (hb_codepoint_t glyph, unsigned int num_glyphs, + code_pair_t *cache = nullptr) const + { + switch (format) + { + case 0: return u.format0.get_sid (glyph, num_glyphs); + case 1: return u.format1.get_sid (glyph, num_glyphs, cache); + case 2: return u.format2.get_sid (glyph, num_glyphs, cache); + default:return 0; + } + } + + void collect_glyph_to_sid_map (glyph_to_sid_map_t *mapping, unsigned int num_glyphs) const + { + switch (format) + { + case 0: u.format0.collect_glyph_to_sid_map (mapping, num_glyphs); return; + case 1: u.format1.collect_glyph_to_sid_map (mapping, num_glyphs); return; + case 2: u.format2.collect_glyph_to_sid_map (mapping, num_glyphs); return; + default:return; + } + } + + hb_codepoint_t get_glyph (hb_codepoint_t sid, unsigned int num_glyphs) const + { + switch (format) + { + case 0: return u.format0.get_glyph (sid, num_glyphs); + case 1: return u.format1.get_glyph (sid, num_glyphs); + case 2: return u.format2.get_glyph (sid, num_glyphs); + default:return 0; + } + } + + bool sanitize (hb_sanitize_context_t *c, unsigned *num_charset_entries) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + + switch (format) + { + case 0: return_trace (u.format0.sanitize (c, c->get_num_glyphs (), num_charset_entries)); + case 1: return_trace (u.format1.sanitize (c, c->get_num_glyphs (), num_charset_entries)); + case 2: return_trace (u.format2.sanitize (c, c->get_num_glyphs (), num_charset_entries)); + default:return_trace (false); + } + } + + HBUINT8 format; + union { + Charset0 format0; + Charset1 format1; + Charset2 format2; + } u; + + DEFINE_SIZE_MIN (1); +}; + +struct CFF1StringIndex : CFF1Index +{ + bool serialize (hb_serialize_context_t *c, const CFF1StringIndex &strings, + const hb_vector_t &sidmap) + { + TRACE_SERIALIZE (this); + if (unlikely ((strings.count == 0) || (sidmap.length == 0))) + { + if (unlikely (!c->extend_min (this->count))) + return_trace (false); + count = 0; + return_trace (true); + } + + if (unlikely (sidmap.in_error ())) return_trace (false); + + // Save this in a vector since serialize() iterates it twice. + hb_vector_t bytesArray (+ hb_iter (sidmap) + | hb_map (strings)); + + if (unlikely (bytesArray.in_error ())) return_trace (false); + + bool result = CFF1Index::serialize (c, bytesArray); + return_trace (result); + } +}; + +struct cff1_top_dict_interp_env_t : num_interp_env_t +{ + cff1_top_dict_interp_env_t () + : num_interp_env_t(), prev_offset(0), last_offset(0) {} + cff1_top_dict_interp_env_t (const hb_ubytes_t &bytes) + : num_interp_env_t(bytes), prev_offset(0), last_offset(0) {} + + unsigned int prev_offset; + unsigned int last_offset; +}; + +struct name_dict_values_t +{ + enum name_dict_val_index_t + { + version, + notice, + copyright, + fullName, + familyName, + weight, + postscript, + fontName, + baseFontName, + registry, + ordering, + + ValCount + }; + + void init () + { + for (unsigned int i = 0; i < ValCount; i++) + values[i] = CFF_UNDEF_SID; + } + + unsigned int& operator[] (unsigned int i) + { assert (i < ValCount); return values[i]; } + + unsigned int operator[] (unsigned int i) const + { assert (i < ValCount); return values[i]; } + + static enum name_dict_val_index_t name_op_to_index (op_code_t op) + { + switch (op) { + default: // can't happen - just make some compiler happy + case OpCode_version: + return version; + case OpCode_Notice: + return notice; + case OpCode_Copyright: + return copyright; + case OpCode_FullName: + return fullName; + case OpCode_FamilyName: + return familyName; + case OpCode_Weight: + return weight; + case OpCode_PostScript: + return postscript; + case OpCode_FontName: + return fontName; + case OpCode_BaseFontName: + return baseFontName; + } + } + + unsigned int values[ValCount]; +}; + +struct cff1_top_dict_val_t : op_str_t +{ + unsigned int last_arg_offset; +}; + +struct cff1_top_dict_values_t : top_dict_values_t +{ + void init () + { + top_dict_values_t::init (); + + nameSIDs.init (); + ros_supplement = 0; + cidCount = 8720; + EncodingOffset = 0; + CharsetOffset = 0; + FDSelectOffset = 0; + privateDictInfo.init (); + } + void fini () { top_dict_values_t::fini (); } + + bool is_CID () const + { return nameSIDs[name_dict_values_t::registry] != CFF_UNDEF_SID; } + + name_dict_values_t nameSIDs; + unsigned int ros_supplement_offset; + unsigned int ros_supplement; + unsigned int cidCount; + + unsigned int EncodingOffset; + unsigned int CharsetOffset; + unsigned int FDSelectOffset; + table_info_t privateDictInfo; +}; + +struct cff1_top_dict_opset_t : top_dict_opset_t +{ + static void process_op (op_code_t op, cff1_top_dict_interp_env_t& env, cff1_top_dict_values_t& dictval) + { + cff1_top_dict_val_t val; + val.last_arg_offset = (env.last_offset-1) - dictval.opStart; /* offset to the last argument */ + + switch (op) { + case OpCode_version: + case OpCode_Notice: + case OpCode_Copyright: + case OpCode_FullName: + case OpCode_FontName: + case OpCode_FamilyName: + case OpCode_Weight: + case OpCode_PostScript: + case OpCode_BaseFontName: + dictval.nameSIDs[name_dict_values_t::name_op_to_index (op)] = env.argStack.pop_uint (); + env.clear_args (); + break; + case OpCode_isFixedPitch: + case OpCode_ItalicAngle: + case OpCode_UnderlinePosition: + case OpCode_UnderlineThickness: + case OpCode_PaintType: + case OpCode_CharstringType: + case OpCode_UniqueID: + case OpCode_StrokeWidth: + case OpCode_SyntheticBase: + case OpCode_CIDFontVersion: + case OpCode_CIDFontRevision: + case OpCode_CIDFontType: + case OpCode_UIDBase: + case OpCode_FontBBox: + case OpCode_XUID: + case OpCode_BaseFontBlend: + env.clear_args (); + break; + + case OpCode_CIDCount: + dictval.cidCount = env.argStack.pop_uint (); + env.clear_args (); + break; + + case OpCode_ROS: + dictval.ros_supplement = env.argStack.pop_uint (); + dictval.nameSIDs[name_dict_values_t::ordering] = env.argStack.pop_uint (); + dictval.nameSIDs[name_dict_values_t::registry] = env.argStack.pop_uint (); + env.clear_args (); + break; + + case OpCode_Encoding: + dictval.EncodingOffset = env.argStack.pop_uint (); + env.clear_args (); + if (unlikely (dictval.EncodingOffset == 0)) return; + break; + + case OpCode_charset: + dictval.CharsetOffset = env.argStack.pop_uint (); + env.clear_args (); + if (unlikely (dictval.CharsetOffset == 0)) return; + break; + + case OpCode_FDSelect: + dictval.FDSelectOffset = env.argStack.pop_uint (); + env.clear_args (); + break; + + case OpCode_Private: + dictval.privateDictInfo.offset = env.argStack.pop_uint (); + dictval.privateDictInfo.size = env.argStack.pop_uint (); + env.clear_args (); + break; + + default: + env.last_offset = env.str_ref.get_offset (); + top_dict_opset_t::process_op (op, env, dictval); + /* Record this operand below if stack is empty, otherwise done */ + if (!env.argStack.is_empty ()) return; + break; + } + + if (unlikely (env.in_error ())) return; + + dictval.add_op (op, env.str_ref, val); + } +}; + +struct cff1_font_dict_values_t : dict_values_t +{ + void init () + { + dict_values_t::init (); + privateDictInfo.init (); + fontName = CFF_UNDEF_SID; + } + void fini () { dict_values_t::fini (); } + + table_info_t privateDictInfo; + unsigned int fontName; +}; + +struct cff1_font_dict_opset_t : dict_opset_t +{ + static void process_op (op_code_t op, num_interp_env_t& env, cff1_font_dict_values_t& dictval) + { + switch (op) { + case OpCode_FontName: + dictval.fontName = env.argStack.pop_uint (); + env.clear_args (); + break; + case OpCode_FontMatrix: + case OpCode_PaintType: + env.clear_args (); + break; + case OpCode_Private: + dictval.privateDictInfo.offset = env.argStack.pop_uint (); + dictval.privateDictInfo.size = env.argStack.pop_uint (); + env.clear_args (); + break; + + default: + dict_opset_t::process_op (op, env); + if (!env.argStack.is_empty ()) return; + break; + } + + if (unlikely (env.in_error ())) return; + + dictval.add_op (op, env.str_ref); + } +}; + +template +struct cff1_private_dict_values_base_t : dict_values_t +{ + void init () + { + dict_values_t::init (); + subrsOffset = 0; + localSubrs = &Null (CFF1Subrs); + } + void fini () { dict_values_t::fini (); } + + unsigned int subrsOffset; + const CFF1Subrs *localSubrs; +}; + +typedef cff1_private_dict_values_base_t cff1_private_dict_values_subset_t; +typedef cff1_private_dict_values_base_t cff1_private_dict_values_t; + +struct cff1_private_dict_opset_t : dict_opset_t +{ + static void process_op (op_code_t op, num_interp_env_t& env, cff1_private_dict_values_t& dictval) + { + num_dict_val_t val; + val.init (); + + switch (op) { + case OpCode_BlueValues: + case OpCode_OtherBlues: + case OpCode_FamilyBlues: + case OpCode_FamilyOtherBlues: + case OpCode_StemSnapH: + case OpCode_StemSnapV: + case OpCode_StdHW: + case OpCode_StdVW: + case OpCode_BlueScale: + case OpCode_BlueShift: + case OpCode_BlueFuzz: + case OpCode_ForceBold: + case OpCode_LanguageGroup: + case OpCode_ExpansionFactor: + case OpCode_initialRandomSeed: + case OpCode_defaultWidthX: + case OpCode_nominalWidthX: + env.clear_args (); + break; + case OpCode_Subrs: + dictval.subrsOffset = env.argStack.pop_uint (); + env.clear_args (); + break; + + default: + dict_opset_t::process_op (op, env); + if (!env.argStack.is_empty ()) return; + break; + } + + if (unlikely (env.in_error ())) return; + + dictval.add_op (op, env.str_ref, val); + } +}; + +struct cff1_private_dict_opset_subset_t : dict_opset_t +{ + static void process_op (op_code_t op, num_interp_env_t& env, cff1_private_dict_values_subset_t& dictval) + { + switch (op) { + case OpCode_BlueValues: + case OpCode_OtherBlues: + case OpCode_FamilyBlues: + case OpCode_FamilyOtherBlues: + case OpCode_StemSnapH: + case OpCode_StemSnapV: + case OpCode_StdHW: + case OpCode_StdVW: + case OpCode_BlueScale: + case OpCode_BlueShift: + case OpCode_BlueFuzz: + case OpCode_ForceBold: + case OpCode_LanguageGroup: + case OpCode_ExpansionFactor: + case OpCode_initialRandomSeed: + case OpCode_defaultWidthX: + case OpCode_nominalWidthX: + env.clear_args (); + break; + + case OpCode_Subrs: + dictval.subrsOffset = env.argStack.pop_uint (); + env.clear_args (); + break; + + default: + dict_opset_t::process_op (op, env); + if (!env.argStack.is_empty ()) return; + break; + } + + if (unlikely (env.in_error ())) return; + + dictval.add_op (op, env.str_ref); + } +}; + +typedef dict_interpreter_t cff1_top_dict_interpreter_t; +typedef dict_interpreter_t cff1_font_dict_interpreter_t; + +typedef CFF1Index CFF1NameIndex; +typedef CFF1Index CFF1TopDictIndex; + +struct cff1_font_dict_values_mod_t +{ + cff1_font_dict_values_mod_t() { init (); } + + void init () { init ( &Null (cff1_font_dict_values_t), CFF_UNDEF_SID ); } + + void init (const cff1_font_dict_values_t *base_, + unsigned int fontName_) + { + base = base_; + fontName = fontName_; + privateDictInfo.init (); + } + + unsigned get_count () const { return base->get_count (); } + + const op_str_t &operator [] (unsigned int i) const { return (*base)[i]; } + + const cff1_font_dict_values_t *base; + table_info_t privateDictInfo; + unsigned int fontName; +}; + +struct CFF1FDArray : FDArray +{ + /* FDArray::serialize() requires this partial specialization to compile */ + template + bool serialize (hb_serialize_context_t *c, ITER it, OP_SERIALIZER& opszr) + { return FDArray::serialize (c, it, opszr); } +}; + +} /* namespace CFF */ + +namespace OT { + +using namespace CFF; + +struct cff1 +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_CFF1; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + likely (version.major == 1)); + } + + template + struct accelerator_templ_t + { + static constexpr hb_tag_t tableTag = cff1::tableTag; + + accelerator_templ_t (hb_face_t *face) + { + if (!face) return; + + topDict.init (); + fontDicts.init (); + privateDicts.init (); + + this->blob = sc.reference_table (face); + + /* setup for run-time santization */ + sc.init (this->blob); + sc.start_processing (); + + const OT::cff1 *cff = this->blob->template as (); + + if (cff == &Null (OT::cff1)) + goto fail; + + nameIndex = &cff->nameIndex (cff); + if ((nameIndex == &Null (CFF1NameIndex)) || !nameIndex->sanitize (&sc)) + goto fail; + + topDictIndex = &StructAtOffset (nameIndex, nameIndex->get_size ()); + if ((topDictIndex == &Null (CFF1TopDictIndex)) || !topDictIndex->sanitize (&sc) || (topDictIndex->count == 0)) + goto fail; + + { /* parse top dict */ + const hb_ubytes_t topDictStr = (*topDictIndex)[0]; + if (unlikely (!topDictStr.sanitize (&sc))) goto fail; + cff1_top_dict_interp_env_t env (topDictStr); + cff1_top_dict_interpreter_t top_interp (env); + if (unlikely (!top_interp.interpret (topDict))) goto fail; + } + + if (is_predef_charset ()) + charset = &Null (Charset); + else + { + charset = &StructAtOffsetOrNull (cff, topDict.CharsetOffset); + if (unlikely ((charset == &Null (Charset)) || !charset->sanitize (&sc, &num_charset_entries))) goto fail; + } + + fdCount = 1; + if (is_CID ()) + { + fdArray = &StructAtOffsetOrNull (cff, topDict.FDArrayOffset); + fdSelect = &StructAtOffsetOrNull (cff, topDict.FDSelectOffset); + if (unlikely ((fdArray == &Null (CFF1FDArray)) || !fdArray->sanitize (&sc) || + (fdSelect == &Null (CFF1FDSelect)) || !fdSelect->sanitize (&sc, fdArray->count))) + goto fail; + + fdCount = fdArray->count; + } + else + { + fdArray = &Null (CFF1FDArray); + fdSelect = &Null (CFF1FDSelect); + } + + encoding = &Null (Encoding); + if (is_CID ()) + { + if (unlikely (charset == &Null (Charset))) goto fail; + } + else + { + if (!is_predef_encoding ()) + { + encoding = &StructAtOffsetOrNull (cff, topDict.EncodingOffset); + if (unlikely ((encoding == &Null (Encoding)) || !encoding->sanitize (&sc))) goto fail; + } + } + + stringIndex = &StructAtOffset (topDictIndex, topDictIndex->get_size ()); + if ((stringIndex == &Null (CFF1StringIndex)) || !stringIndex->sanitize (&sc)) + goto fail; + + globalSubrs = &StructAtOffset (stringIndex, stringIndex->get_size ()); + if ((globalSubrs != &Null (CFF1Subrs)) && !globalSubrs->sanitize (&sc)) + goto fail; + + charStrings = &StructAtOffsetOrNull (cff, topDict.charStringsOffset); + + if ((charStrings == &Null (CFF1CharStrings)) || unlikely (!charStrings->sanitize (&sc))) + goto fail; + + num_glyphs = charStrings->count; + if (num_glyphs != sc.get_num_glyphs ()) + goto fail; + + if (unlikely (!privateDicts.resize (fdCount))) + goto fail; + for (unsigned int i = 0; i < fdCount; i++) + privateDicts[i].init (); + + // parse CID font dicts and gather private dicts + if (is_CID ()) + { + for (unsigned int i = 0; i < fdCount; i++) + { + hb_ubytes_t fontDictStr = (*fdArray)[i]; + if (unlikely (!fontDictStr.sanitize (&sc))) goto fail; + cff1_font_dict_values_t *font; + cff1_top_dict_interp_env_t env (fontDictStr); + cff1_font_dict_interpreter_t font_interp (env); + font = fontDicts.push (); + if (unlikely (fontDicts.in_error ())) goto fail; + + font->init (); + if (unlikely (!font_interp.interpret (*font))) goto fail; + PRIVDICTVAL *priv = &privateDicts[i]; + const hb_ubytes_t privDictStr = StructAtOffset (cff, font->privateDictInfo.offset).as_ubytes (font->privateDictInfo.size); + if (unlikely (!privDictStr.sanitize (&sc))) goto fail; + num_interp_env_t env2 (privDictStr); + dict_interpreter_t priv_interp (env2); + priv->init (); + if (unlikely (!priv_interp.interpret (*priv))) goto fail; + + priv->localSubrs = &StructAtOffsetOrNull (&privDictStr, priv->subrsOffset); + if (priv->localSubrs != &Null (CFF1Subrs) && + unlikely (!priv->localSubrs->sanitize (&sc))) + goto fail; + } + } + else /* non-CID */ + { + cff1_top_dict_values_t *font = &topDict; + PRIVDICTVAL *priv = &privateDicts[0]; + + const hb_ubytes_t privDictStr = StructAtOffset (cff, font->privateDictInfo.offset).as_ubytes (font->privateDictInfo.size); + if (unlikely (!privDictStr.sanitize (&sc))) goto fail; + num_interp_env_t env (privDictStr); + dict_interpreter_t priv_interp (env); + priv->init (); + if (unlikely (!priv_interp.interpret (*priv))) goto fail; + + priv->localSubrs = &StructAtOffsetOrNull (&privDictStr, priv->subrsOffset); + if (priv->localSubrs != &Null (CFF1Subrs) && + unlikely (!priv->localSubrs->sanitize (&sc))) + goto fail; + } + + return; + + fail: + _fini (); + } + ~accelerator_templ_t () { _fini (); } + void _fini () + { + sc.end_processing (); + topDict.fini (); + fontDicts.fini (); + privateDicts.fini (); + hb_blob_destroy (blob); + blob = nullptr; + } + + hb_blob_t *get_blob () const { return blob; } + + bool is_valid () const { return blob; } + bool is_CID () const { return topDict.is_CID (); } + + bool is_predef_charset () const { return topDict.CharsetOffset <= ExpertSubsetCharset; } + + unsigned int std_code_to_glyph (hb_codepoint_t code) const + { + hb_codepoint_t sid = lookup_standard_encoding_for_sid (code); + if (unlikely (sid == CFF_UNDEF_SID)) + return 0; + + if (charset != &Null (Charset)) + return charset->get_glyph (sid, num_glyphs); + else if ((topDict.CharsetOffset == ISOAdobeCharset) + && (code <= 228 /*zcaron*/)) return sid; + return 0; + } + + bool is_predef_encoding () const { return topDict.EncodingOffset <= ExpertEncoding; } + + hb_codepoint_t glyph_to_code (hb_codepoint_t glyph, + code_pair_t *glyph_to_sid_cache = nullptr) const + { + if (encoding != &Null (Encoding)) + return encoding->get_code (glyph); + else + { + hb_codepoint_t sid = glyph_to_sid (glyph, glyph_to_sid_cache); + if (sid == 0) return 0; + hb_codepoint_t code = 0; + switch (topDict.EncodingOffset) + { + case StandardEncoding: + code = lookup_standard_encoding_for_code (sid); + break; + case ExpertEncoding: + code = lookup_expert_encoding_for_code (sid); + break; + default: + break; + } + return code; + } + } + + glyph_to_sid_map_t *create_glyph_to_sid_map () const + { + if (charset != &Null (Charset)) + { + auto *mapping = (glyph_to_sid_map_t *) hb_malloc (sizeof (glyph_to_sid_map_t)); + if (unlikely (!mapping)) return nullptr; + mapping = new (mapping) glyph_to_sid_map_t (); + mapping->push (code_pair_t {0, 1}); + charset->collect_glyph_to_sid_map (mapping, num_glyphs); + return mapping; + } + else + return nullptr; + } + + hb_codepoint_t glyph_to_sid (hb_codepoint_t glyph, + code_pair_t *cache = nullptr) const + { + if (charset != &Null (Charset)) + return charset->get_sid (glyph, num_glyphs, cache); + else + { + hb_codepoint_t sid = 0; + switch (topDict.CharsetOffset) + { + case ISOAdobeCharset: + if (glyph <= 228 /*zcaron*/) sid = glyph; + break; + case ExpertCharset: + sid = lookup_expert_charset_for_sid (glyph); + break; + case ExpertSubsetCharset: + sid = lookup_expert_subset_charset_for_sid (glyph); + break; + default: + break; + } + return sid; + } + } + + hb_codepoint_t sid_to_glyph (hb_codepoint_t sid) const + { + if (charset != &Null (Charset)) + return charset->get_glyph (sid, num_glyphs); + else + { + hb_codepoint_t glyph = 0; + switch (topDict.CharsetOffset) + { + case ISOAdobeCharset: + if (sid <= 228 /*zcaron*/) glyph = sid; + break; + case ExpertCharset: + glyph = lookup_expert_charset_for_glyph (sid); + break; + case ExpertSubsetCharset: + glyph = lookup_expert_subset_charset_for_glyph (sid); + break; + default: + break; + } + return glyph; + } + } + + protected: + hb_sanitize_context_t sc; + + public: + hb_blob_t *blob = nullptr; + const Encoding *encoding = nullptr; + const Charset *charset = nullptr; + const CFF1NameIndex *nameIndex = nullptr; + const CFF1TopDictIndex *topDictIndex = nullptr; + const CFF1StringIndex *stringIndex = nullptr; + const CFF1Subrs *globalSubrs = nullptr; + const CFF1CharStrings *charStrings = nullptr; + const CFF1FDArray *fdArray = nullptr; + const CFF1FDSelect *fdSelect = nullptr; + unsigned int fdCount = 0; + + cff1_top_dict_values_t topDict; + hb_vector_t + fontDicts; + hb_vector_t privateDicts; + + unsigned int num_glyphs = 0; + unsigned int num_charset_entries = 0; + }; + + struct accelerator_t : accelerator_templ_t + { + accelerator_t (hb_face_t *face) : SUPER (face) + { + glyph_names.set_relaxed (nullptr); + + if (!is_valid ()) return; + if (is_CID ()) return; + } + ~accelerator_t () + { + hb_sorted_vector_t *names = glyph_names.get_relaxed (); + if (names) + { + names->fini (); + hb_free (names); + } + } + + bool get_glyph_name (hb_codepoint_t glyph, + char *buf, unsigned int buf_len) const + { + if (unlikely (glyph >= num_glyphs)) return false; + if (unlikely (!is_valid ())) return false; + if (is_CID()) return false; + if (unlikely (!buf_len)) return true; + hb_codepoint_t sid = glyph_to_sid (glyph); + const char *str; + size_t str_len; + if (sid < cff1_std_strings_length) + { + hb_bytes_t byte_str = cff1_std_strings (sid); + str = byte_str.arrayZ; + str_len = byte_str.length; + } + else + { + hb_ubytes_t ubyte_str = (*stringIndex)[sid - cff1_std_strings_length]; + str = (const char *)ubyte_str.arrayZ; + str_len = ubyte_str.length; + } + if (!str_len) return false; + unsigned int len = hb_min (buf_len - 1, str_len); + strncpy (buf, (const char*)str, len); + buf[len] = '\0'; + return true; + } + + bool get_glyph_from_name (const char *name, int len, + hb_codepoint_t *glyph) const + { + if (unlikely (!is_valid ())) return false; + if (is_CID()) return false; + if (len < 0) len = strlen (name); + if (unlikely (!len)) return false; + + retry: + hb_sorted_vector_t *names = glyph_names.get_acquire (); + if (unlikely (!names)) + { + names = (hb_sorted_vector_t *) hb_calloc (sizeof (hb_sorted_vector_t), 1); + if (likely (names)) + { + names->init (); + /* TODO */ + + /* fill glyph names */ + code_pair_t glyph_to_sid_cache {0, HB_CODEPOINT_INVALID}; + for (hb_codepoint_t gid = 0; gid < num_glyphs; gid++) + { + hb_codepoint_t sid = glyph_to_sid (gid, &glyph_to_sid_cache); + gname_t gname; + gname.sid = sid; + if (sid < cff1_std_strings_length) + gname.name = cff1_std_strings (sid); + else + { + hb_ubytes_t ustr = (*stringIndex)[sid - cff1_std_strings_length]; + gname.name = hb_bytes_t ((const char*) ustr.arrayZ, ustr.length); + } + if (unlikely (!gname.name.arrayZ)) + gname.name = hb_bytes_t ("", 0); /* To avoid nullptr. */ + names->push (gname); + } + names->qsort (); + } + if (unlikely (!glyph_names.cmpexch (nullptr, names))) + { + if (names) + { + names->fini (); + hb_free (names); + } + goto retry; + } + } + + gname_t key = { hb_bytes_t (name, len), 0 }; + const gname_t *gname = names ? names->bsearch (key) : nullptr; + if (!gname) return false; + hb_codepoint_t gid = sid_to_glyph (gname->sid); + if (!gid && gname->sid) return false; + *glyph = gid; + return true; + } + + HB_INTERNAL bool get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const; + HB_INTERNAL bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const; + HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const; + + private: + struct gname_t + { + hb_bytes_t name; + uint16_t sid; + + static int cmp (const void *a_, const void *b_) + { + const gname_t *a = (const gname_t *)a_; + const gname_t *b = (const gname_t *)b_; + unsigned minlen = hb_min (a->name.length, b->name.length); + int ret = strncmp (a->name.arrayZ, b->name.arrayZ, minlen); + if (ret) return ret; + return a->name.length - b->name.length; + } + + int cmp (const gname_t &a) const { return cmp (&a, this); } + }; + + mutable hb_atomic_ptr_t> glyph_names; + + typedef accelerator_templ_t SUPER; + }; + + struct accelerator_subset_t : accelerator_templ_t + { + accelerator_subset_t (hb_face_t *face) : SUPER (face) {} + ~accelerator_subset_t () + { + if (cff_accelerator) + cff_subset_accelerator_t::destroy (cff_accelerator); + } + + HB_INTERNAL bool subset (hb_subset_context_t *c) const; + HB_INTERNAL bool serialize (hb_serialize_context_t *c, + struct cff1_subset_plan &plan) const; + HB_INTERNAL bool get_seac_components (hb_codepoint_t glyph, hb_codepoint_t *base, hb_codepoint_t *accent) const; + + mutable CFF::cff_subset_accelerator_t* cff_accelerator = nullptr; + + typedef accelerator_templ_t SUPER; + }; + + protected: + HB_INTERNAL static hb_codepoint_t lookup_standard_encoding_for_code (hb_codepoint_t sid); + HB_INTERNAL static hb_codepoint_t lookup_expert_encoding_for_code (hb_codepoint_t sid); + HB_INTERNAL static hb_codepoint_t lookup_expert_charset_for_sid (hb_codepoint_t glyph); + HB_INTERNAL static hb_codepoint_t lookup_expert_subset_charset_for_sid (hb_codepoint_t glyph); + HB_INTERNAL static hb_codepoint_t lookup_expert_charset_for_glyph (hb_codepoint_t sid); + HB_INTERNAL static hb_codepoint_t lookup_expert_subset_charset_for_glyph (hb_codepoint_t sid); + HB_INTERNAL static hb_codepoint_t lookup_standard_encoding_for_sid (hb_codepoint_t code); + + public: + FixedVersion version; /* Version of CFF table. set to 0x0100u */ + NNOffsetTo nameIndex; /* headerSize = Offset to Name INDEX. */ + HBUINT8 offSize; /* offset size (unused?) */ + + public: + DEFINE_SIZE_STATIC (4); +}; + +struct cff1_accelerator_t : cff1::accelerator_t { + cff1_accelerator_t (hb_face_t *face) : cff1::accelerator_t (face) {} +}; + +struct cff1_subset_accelerator_t : cff1::accelerator_subset_t { + cff1_subset_accelerator_t (hb_face_t *face) : cff1::accelerator_subset_t (face) {} +}; + +} /* namespace OT */ + +#endif /* HB_OT_CFF1_TABLE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-cff2-table.cc b/src/3rdparty/harfbuzz-ng/src/hb-ot-cff2-table.cc new file mode 100644 index 00000000000..79555655519 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-cff2-table.cc @@ -0,0 +1,222 @@ +/* + * Copyright © 2018 Adobe 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. + * + * Adobe Author(s): Michiharu Ariza + */ + +#include "hb.hh" + +#ifndef HB_NO_OT_FONT_CFF + +#include "hb-ot-cff2-table.hh" +#include "hb-cff2-interp-cs.hh" +#include "hb-draw.hh" + +using namespace CFF; + +struct cff2_extents_param_t +{ + cff2_extents_param_t () + { + min_x.set_int (INT_MAX); + min_y.set_int (INT_MAX); + max_x.set_int (INT_MIN); + max_y.set_int (INT_MIN); + } + + void start_path () { path_open = true; } + void end_path () { path_open = false; } + bool is_path_open () const { return path_open; } + + void update_bounds (const point_t &pt) + { + if (pt.x < min_x) min_x = pt.x; + if (pt.x > max_x) max_x = pt.x; + if (pt.y < min_y) min_y = pt.y; + if (pt.y > max_y) max_y = pt.y; + } + + bool path_open = false; + number_t min_x; + number_t min_y; + number_t max_x; + number_t max_y; +}; + +struct cff2_path_procs_extents_t : path_procs_t, cff2_extents_param_t> +{ + static void moveto (cff2_cs_interp_env_t &env, cff2_extents_param_t& param, const point_t &pt) + { + param.end_path (); + env.moveto (pt); + } + + static void line (cff2_cs_interp_env_t &env, cff2_extents_param_t& param, const point_t &pt1) + { + if (!param.is_path_open ()) + { + param.start_path (); + param.update_bounds (env.get_pt ()); + } + env.moveto (pt1); + param.update_bounds (env.get_pt ()); + } + + static void curve (cff2_cs_interp_env_t &env, cff2_extents_param_t& param, const point_t &pt1, const point_t &pt2, const point_t &pt3) + { + if (!param.is_path_open ()) + { + param.start_path (); + param.update_bounds (env.get_pt ()); + } + /* include control points */ + param.update_bounds (pt1); + param.update_bounds (pt2); + env.moveto (pt3); + param.update_bounds (env.get_pt ()); + } +}; + +struct cff2_cs_opset_extents_t : cff2_cs_opset_t {}; + +bool OT::cff2::accelerator_t::get_extents (hb_font_t *font, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents) const +{ +#ifdef HB_NO_OT_FONT_CFF + /* XXX Remove check when this code moves to .hh file. */ + return true; +#endif + + if (unlikely (!is_valid () || (glyph >= num_glyphs))) return false; + + unsigned int fd = fdSelect->get_fd (glyph); + const hb_ubytes_t str = (*charStrings)[glyph]; + cff2_cs_interp_env_t env (str, *this, fd, font->coords, font->num_coords); + cff2_cs_interpreter_t interp (env); + cff2_extents_param_t param; + if (unlikely (!interp.interpret (param))) return false; + + if (param.min_x >= param.max_x) + { + extents->width = 0; + extents->x_bearing = 0; + } + else + { + extents->x_bearing = roundf (param.min_x.to_real ()); + extents->width = roundf (param.max_x.to_real () - extents->x_bearing); + } + if (param.min_y >= param.max_y) + { + extents->height = 0; + extents->y_bearing = 0; + } + else + { + extents->y_bearing = roundf (param.max_y.to_real ()); + extents->height = roundf (param.min_y.to_real () - extents->y_bearing); + } + + font->scale_glyph_extents (extents); + + return true; +} + +bool OT::cff2::accelerator_t::paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const +{ + funcs->push_clip_glyph (data, glyph, font); + funcs->color (data, true, foreground); + funcs->pop_clip (data); + + return true; +} + +struct cff2_path_param_t +{ + cff2_path_param_t (hb_font_t *font_, hb_draw_session_t &draw_session_) + { + draw_session = &draw_session_; + font = font_; + } + + void move_to (const point_t &p) + { draw_session->move_to (font->em_fscalef_x (p.x.to_real ()), font->em_fscalef_y (p.y.to_real ())); } + + void line_to (const point_t &p) + { draw_session->line_to (font->em_fscalef_x (p.x.to_real ()), font->em_fscalef_y (p.y.to_real ())); } + + void cubic_to (const point_t &p1, const point_t &p2, const point_t &p3) + { + draw_session->cubic_to (font->em_fscalef_x (p1.x.to_real ()), font->em_fscalef_y (p1.y.to_real ()), + font->em_fscalef_x (p2.x.to_real ()), font->em_fscalef_y (p2.y.to_real ()), + font->em_fscalef_x (p3.x.to_real ()), font->em_fscalef_y (p3.y.to_real ())); + } + + protected: + hb_draw_session_t *draw_session; + hb_font_t *font; +}; + +struct cff2_path_procs_path_t : path_procs_t, cff2_path_param_t> +{ + static void moveto (cff2_cs_interp_env_t &env, cff2_path_param_t& param, const point_t &pt) + { + param.move_to (pt); + env.moveto (pt); + } + + static void line (cff2_cs_interp_env_t &env, cff2_path_param_t& param, const point_t &pt1) + { + param.line_to (pt1); + env.moveto (pt1); + } + + static void curve (cff2_cs_interp_env_t &env, cff2_path_param_t& param, const point_t &pt1, const point_t &pt2, const point_t &pt3) + { + param.cubic_to (pt1, pt2, pt3); + env.moveto (pt3); + } +}; + +struct cff2_cs_opset_path_t : cff2_cs_opset_t {}; + +bool OT::cff2::accelerator_t::get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const +{ +#ifdef HB_NO_OT_FONT_CFF + /* XXX Remove check when this code moves to .hh file. */ + return true; +#endif + + if (unlikely (!is_valid () || (glyph >= num_glyphs))) return false; + + unsigned int fd = fdSelect->get_fd (glyph); + const hb_ubytes_t str = (*charStrings)[glyph]; + cff2_cs_interp_env_t env (str, *this, fd, font->coords, font->num_coords); + cff2_cs_interpreter_t interp (env); + cff2_path_param_t param (font, draw_session); + if (unlikely (!interp.interpret (param))) return false; + return true; +} + +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-cff2-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-ot-cff2-table.hh new file mode 100644 index 00000000000..af24bb9986f --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-cff2-table.hh @@ -0,0 +1,563 @@ +/* + * Copyright © 2018 Adobe 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. + * + * Adobe Author(s): Michiharu Ariza + */ + +#ifndef HB_OT_CFF2_TABLE_HH +#define HB_OT_CFF2_TABLE_HH + +#include "hb-ot-cff-common.hh" +#include "hb-subset-cff-common.hh" +#include "hb-draw.hh" +#include "hb-paint.hh" + +namespace CFF { + +/* + * CFF2 -- Compact Font Format (CFF) Version 2 + * https://docs.microsoft.com/en-us/typography/opentype/spec/cff2 + */ +#define HB_OT_TAG_CFF2 HB_TAG('C','F','F','2') + +typedef CFFIndex CFF2Index; + +typedef CFF2Index CFF2CharStrings; +typedef Subrs CFF2Subrs; + +typedef FDSelect3_4 FDSelect4; +typedef FDSelect3_4_Range FDSelect4_Range; + +struct CFF2FDSelect +{ + bool serialize (hb_serialize_context_t *c, const CFF2FDSelect &src, unsigned int num_glyphs) + { + TRACE_SERIALIZE (this); + unsigned int size = src.get_size (num_glyphs); + CFF2FDSelect *dest = c->allocate_size (size); + if (unlikely (!dest)) return_trace (false); + hb_memcpy (dest, &src, size); + return_trace (true); + } + + unsigned int get_size (unsigned int num_glyphs) const + { + switch (format) + { + case 0: return format.static_size + u.format0.get_size (num_glyphs); + case 3: return format.static_size + u.format3.get_size (); + case 4: return format.static_size + u.format4.get_size (); + default:return 0; + } + } + + hb_codepoint_t get_fd (hb_codepoint_t glyph) const + { + if (this == &Null (CFF2FDSelect)) + return 0; + + switch (format) + { + case 0: return u.format0.get_fd (glyph); + case 3: return u.format3.get_fd (glyph); + case 4: return u.format4.get_fd (glyph); + default:return 0; + } + } + + bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + + switch (format) + { + case 0: return_trace (u.format0.sanitize (c, fdcount)); + case 3: return_trace (u.format3.sanitize (c, fdcount)); + case 4: return_trace (u.format4.sanitize (c, fdcount)); + default:return_trace (false); + } + } + + HBUINT8 format; + union { + FDSelect0 format0; + FDSelect3 format3; + FDSelect4 format4; + } u; + public: + DEFINE_SIZE_MIN (2); +}; + +struct CFF2VariationStore +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this)) && c->check_range (&varStore, size) && varStore.sanitize (c)); + } + + bool serialize (hb_serialize_context_t *c, const CFF2VariationStore *varStore) + { + TRACE_SERIALIZE (this); + unsigned int size_ = varStore->get_size (); + CFF2VariationStore *dest = c->allocate_size (size_); + if (unlikely (!dest)) return_trace (false); + hb_memcpy (dest, varStore, size_); + return_trace (true); + } + + unsigned int get_size () const { return HBUINT16::static_size + size; } + + HBUINT16 size; + VariationStore varStore; + + DEFINE_SIZE_MIN (2 + VariationStore::min_size); +}; + +struct cff2_top_dict_values_t : top_dict_values_t<> +{ + void init () + { + top_dict_values_t<>::init (); + vstoreOffset = 0; + FDSelectOffset = 0; + } + void fini () { top_dict_values_t<>::fini (); } + + unsigned int vstoreOffset; + unsigned int FDSelectOffset; +}; + +struct cff2_top_dict_opset_t : top_dict_opset_t<> +{ + static void process_op (op_code_t op, num_interp_env_t& env, cff2_top_dict_values_t& dictval) + { + switch (op) { + case OpCode_FontMatrix: + { + dict_val_t val; + val.init (); + dictval.add_op (op, env.str_ref); + env.clear_args (); + } + break; + + case OpCode_vstore: + dictval.vstoreOffset = env.argStack.pop_uint (); + env.clear_args (); + break; + case OpCode_FDSelect: + dictval.FDSelectOffset = env.argStack.pop_uint (); + env.clear_args (); + break; + + default: + SUPER::process_op (op, env, dictval); + /* Record this operand below if stack is empty, otherwise done */ + if (!env.argStack.is_empty ()) return; + } + + if (unlikely (env.in_error ())) return; + + dictval.add_op (op, env.str_ref); + } + + typedef top_dict_opset_t<> SUPER; +}; + +struct cff2_font_dict_values_t : dict_values_t +{ + void init () + { + dict_values_t::init (); + privateDictInfo.init (); + } + void fini () { dict_values_t::fini (); } + + table_info_t privateDictInfo; +}; + +struct cff2_font_dict_opset_t : dict_opset_t +{ + static void process_op (op_code_t op, num_interp_env_t& env, cff2_font_dict_values_t& dictval) + { + switch (op) { + case OpCode_Private: + dictval.privateDictInfo.offset = env.argStack.pop_uint (); + dictval.privateDictInfo.size = env.argStack.pop_uint (); + env.clear_args (); + break; + + default: + SUPER::process_op (op, env); + if (!env.argStack.is_empty ()) + return; + } + + if (unlikely (env.in_error ())) return; + + dictval.add_op (op, env.str_ref); + } + + private: + typedef dict_opset_t SUPER; +}; + +template +struct cff2_private_dict_values_base_t : dict_values_t +{ + void init () + { + dict_values_t::init (); + subrsOffset = 0; + localSubrs = &Null (CFF2Subrs); + ivs = 0; + } + void fini () { dict_values_t::fini (); } + + unsigned int subrsOffset; + const CFF2Subrs *localSubrs; + unsigned int ivs; +}; + +typedef cff2_private_dict_values_base_t cff2_private_dict_values_subset_t; +typedef cff2_private_dict_values_base_t cff2_private_dict_values_t; + +struct cff2_priv_dict_interp_env_t : num_interp_env_t +{ + cff2_priv_dict_interp_env_t (const hb_ubytes_t &str) : + num_interp_env_t (str) {} + + void process_vsindex () + { + if (likely (!seen_vsindex)) + { + set_ivs (argStack.pop_uint ()); + } + seen_vsindex = true; + } + + unsigned int get_ivs () const { return ivs; } + void set_ivs (unsigned int ivs_) { ivs = ivs_; } + + protected: + unsigned int ivs = 0; + bool seen_vsindex = false; +}; + +struct cff2_private_dict_opset_t : dict_opset_t +{ + static void process_op (op_code_t op, cff2_priv_dict_interp_env_t& env, cff2_private_dict_values_t& dictval) + { + num_dict_val_t val; + val.init (); + + switch (op) { + case OpCode_StdHW: + case OpCode_StdVW: + case OpCode_BlueScale: + case OpCode_BlueShift: + case OpCode_BlueFuzz: + case OpCode_ExpansionFactor: + case OpCode_LanguageGroup: + case OpCode_BlueValues: + case OpCode_OtherBlues: + case OpCode_FamilyBlues: + case OpCode_FamilyOtherBlues: + case OpCode_StemSnapH: + case OpCode_StemSnapV: + env.clear_args (); + break; + case OpCode_Subrs: + dictval.subrsOffset = env.argStack.pop_uint (); + env.clear_args (); + break; + case OpCode_vsindexdict: + env.process_vsindex (); + dictval.ivs = env.get_ivs (); + env.clear_args (); + break; + case OpCode_blenddict: + break; + + default: + dict_opset_t::process_op (op, env); + if (!env.argStack.is_empty ()) return; + break; + } + + if (unlikely (env.in_error ())) return; + + dictval.add_op (op, env.str_ref, val); + } +}; + +struct cff2_private_dict_opset_subset_t : dict_opset_t +{ + static void process_op (op_code_t op, cff2_priv_dict_interp_env_t& env, cff2_private_dict_values_subset_t& dictval) + { + switch (op) { + case OpCode_BlueValues: + case OpCode_OtherBlues: + case OpCode_FamilyBlues: + case OpCode_FamilyOtherBlues: + case OpCode_StdHW: + case OpCode_StdVW: + case OpCode_BlueScale: + case OpCode_BlueShift: + case OpCode_BlueFuzz: + case OpCode_StemSnapH: + case OpCode_StemSnapV: + case OpCode_LanguageGroup: + case OpCode_ExpansionFactor: + env.clear_args (); + break; + + case OpCode_blenddict: + env.clear_args (); + return; + + case OpCode_Subrs: + dictval.subrsOffset = env.argStack.pop_uint (); + env.clear_args (); + break; + + default: + SUPER::process_op (op, env); + if (!env.argStack.is_empty ()) return; + break; + } + + if (unlikely (env.in_error ())) return; + + dictval.add_op (op, env.str_ref); + } + + private: + typedef dict_opset_t SUPER; +}; + +typedef dict_interpreter_t cff2_top_dict_interpreter_t; +typedef dict_interpreter_t cff2_font_dict_interpreter_t; + +struct CFF2FDArray : FDArray +{ + /* FDArray::serialize does not compile without this partial specialization */ + template + bool serialize (hb_serialize_context_t *c, ITER it, OP_SERIALIZER& opszr) + { return FDArray::serialize (c, it, opszr); } +}; + +} /* namespace CFF */ + +namespace OT { + +using namespace CFF; + +struct cff2 +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_CFF2; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + likely (version.major == 2)); + } + + template + struct accelerator_templ_t + { + static constexpr hb_tag_t tableTag = cff2::tableTag; + + accelerator_templ_t (hb_face_t *face) + { + if (!face) return; + + topDict.init (); + fontDicts.init (); + privateDicts.init (); + + this->blob = sc.reference_table (face); + + /* setup for run-time santization */ + sc.init (this->blob); + sc.start_processing (); + + const OT::cff2 *cff2 = this->blob->template as (); + + if (cff2 == &Null (OT::cff2)) + goto fail; + + { /* parse top dict */ + hb_ubytes_t topDictStr = (cff2 + cff2->topDict).as_ubytes (cff2->topDictSize); + if (unlikely (!topDictStr.sanitize (&sc))) goto fail; + num_interp_env_t env (topDictStr); + cff2_top_dict_interpreter_t top_interp (env); + topDict.init (); + if (unlikely (!top_interp.interpret (topDict))) goto fail; + } + + globalSubrs = &StructAtOffset (cff2, cff2->topDict + cff2->topDictSize); + varStore = &StructAtOffsetOrNull (cff2, topDict.vstoreOffset); + charStrings = &StructAtOffsetOrNull (cff2, topDict.charStringsOffset); + fdArray = &StructAtOffsetOrNull (cff2, topDict.FDArrayOffset); + fdSelect = &StructAtOffsetOrNull (cff2, topDict.FDSelectOffset); + + if (((varStore != &Null (CFF2VariationStore)) && unlikely (!varStore->sanitize (&sc))) || + (charStrings == &Null (CFF2CharStrings)) || unlikely (!charStrings->sanitize (&sc)) || + (globalSubrs == &Null (CFF2Subrs)) || unlikely (!globalSubrs->sanitize (&sc)) || + (fdArray == &Null (CFF2FDArray)) || unlikely (!fdArray->sanitize (&sc)) || + (((fdSelect != &Null (CFF2FDSelect)) && unlikely (!fdSelect->sanitize (&sc, fdArray->count))))) + goto fail; + + num_glyphs = charStrings->count; + if (num_glyphs != sc.get_num_glyphs ()) + goto fail; + + fdCount = fdArray->count; + if (!privateDicts.resize (fdCount)) + goto fail; + + /* parse font dicts and gather private dicts */ + for (unsigned int i = 0; i < fdCount; i++) + { + const hb_ubytes_t fontDictStr = (*fdArray)[i]; + if (unlikely (!fontDictStr.sanitize (&sc))) goto fail; + cff2_font_dict_values_t *font; + num_interp_env_t env (fontDictStr); + cff2_font_dict_interpreter_t font_interp (env); + font = fontDicts.push (); + if (unlikely (font == &Crap (cff2_font_dict_values_t))) goto fail; + font->init (); + if (unlikely (!font_interp.interpret (*font))) goto fail; + + const hb_ubytes_t privDictStr = StructAtOffsetOrNull (cff2, font->privateDictInfo.offset).as_ubytes (font->privateDictInfo.size); + if (unlikely (!privDictStr.sanitize (&sc))) goto fail; + cff2_priv_dict_interp_env_t env2 (privDictStr); + dict_interpreter_t priv_interp (env2); + privateDicts[i].init (); + if (unlikely (!priv_interp.interpret (privateDicts[i]))) goto fail; + + privateDicts[i].localSubrs = &StructAtOffsetOrNull (&privDictStr[0], privateDicts[i].subrsOffset); + if (privateDicts[i].localSubrs != &Null (CFF2Subrs) && + unlikely (!privateDicts[i].localSubrs->sanitize (&sc))) + goto fail; + } + + return; + + fail: + _fini (); + } + ~accelerator_templ_t () { _fini (); } + void _fini () + { + sc.end_processing (); + topDict.fini (); + fontDicts.fini (); + privateDicts.fini (); + hb_blob_destroy (blob); + blob = nullptr; + } + + hb_vector_t *create_glyph_to_sid_map () const + { + return nullptr; + } + + hb_blob_t *get_blob () const { return blob; } + + bool is_valid () const { return blob; } + + protected: + hb_sanitize_context_t sc; + + public: + hb_blob_t *blob = nullptr; + cff2_top_dict_values_t topDict; + const CFF2Subrs *globalSubrs = nullptr; + const CFF2VariationStore *varStore = nullptr; + const CFF2CharStrings *charStrings = nullptr; + const CFF2FDArray *fdArray = nullptr; + const CFF2FDSelect *fdSelect = nullptr; + unsigned int fdCount = 0; + + hb_vector_t fontDicts; + hb_vector_t privateDicts; + + unsigned int num_glyphs = 0; + }; + + struct accelerator_t : accelerator_templ_t + { + accelerator_t (hb_face_t *face) : accelerator_templ_t (face) {} + + HB_INTERNAL bool get_extents (hb_font_t *font, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents) const; + HB_INTERNAL bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const; + HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const; + }; + + struct accelerator_subset_t : accelerator_templ_t + { + accelerator_subset_t (hb_face_t *face) : SUPER (face) {} + ~accelerator_subset_t () + { + if (cff_accelerator) + cff_subset_accelerator_t::destroy (cff_accelerator); + } + + HB_INTERNAL bool subset (hb_subset_context_t *c) const; + HB_INTERNAL bool serialize (hb_serialize_context_t *c, + struct cff2_subset_plan &plan, + hb_array_t normalized_coords) const; + + mutable CFF::cff_subset_accelerator_t* cff_accelerator = nullptr; + + typedef accelerator_templ_t SUPER; + }; + + public: + FixedVersion version; /* Version of CFF2 table. set to 0x0200u */ + NNOffsetTo topDict; /* headerSize = Offset to Top DICT. */ + HBUINT16 topDictSize; /* Top DICT size */ + + public: + DEFINE_SIZE_STATIC (5); +}; + +struct cff2_accelerator_t : cff2::accelerator_t { + cff2_accelerator_t (hb_face_t *face) : cff2::accelerator_t (face) {} +}; + +struct cff2_subset_accelerator_t : cff2::accelerator_subset_t { + cff2_subset_accelerator_t (hb_face_t *face) : cff2::accelerator_subset_t (face) {} +}; + +} /* namespace OT */ + +#endif /* HB_OT_CFF2_TABLE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-cmap-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-ot-cmap-table.hh index 883d7b3f0b2..30401b1926e 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-ot-cmap-table.hh +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-cmap-table.hh @@ -27,41 +27,67 @@ #ifndef HB_OT_CMAP_TABLE_HH #define HB_OT_CMAP_TABLE_HH -#include "hb-open-type-private.hh" +#include "hb-ot-os2-table.hh" +#include "hb-ot-shaper-arabic-pua.hh" +#include "hb-open-type.hh" +#include "hb-set.hh" +#include "hb-cache.hh" +/* + * cmap -- Character to Glyph Index Mapping + * https://docs.microsoft.com/en-us/typography/opentype/spec/cmap + */ +#define HB_OT_TAG_cmap HB_TAG('c','m','a','p') namespace OT { -/* - * cmap -- Character To Glyph Index Mapping Table - */ - -#define HB_OT_TAG_cmap HB_TAG('c','m','a','p') - - struct CmapSubtableFormat0 { - inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const + bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const { hb_codepoint_t gid = codepoint < 256 ? glyphIdArray[codepoint] : 0; - if (!gid) + if (unlikely (!gid)) return false; *glyph = gid; return true; } - inline bool sanitize (hb_sanitize_context_t *c) const + unsigned get_language () const + { + return language; + } + + void collect_unicodes (hb_set_t *out) const + { + for (unsigned int i = 0; i < 256; i++) + if (glyphIdArray[i]) + out->add (i); + } + + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const + { + for (unsigned i = 0; i < 256; i++) + if (glyphIdArray[i]) + { + hb_codepoint_t glyph = glyphIdArray[i]; + unicodes->add (i); + mapping->set (i, glyph); + } + } + + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this)); } protected: - UINT16 format; /* Format number is set to 0. */ - UINT16 lengthZ; /* Byte length of this subtable. */ - UINT16 languageZ; /* Ignore. */ - UINT8 glyphIdArray[256];/* An array that maps character + HBUINT16 format; /* Format number is set to 0. */ + HBUINT16 length; /* Byte length of this subtable. */ + HBUINT16 language; /* Ignore. */ + HBUINT8 glyphIdArray[256];/* An array that maps character * code to glyph index values. */ public: DEFINE_SIZE_STATIC (6 + 256); @@ -69,12 +95,300 @@ struct CmapSubtableFormat0 struct CmapSubtableFormat4 { + + + template + void to_ranges (Iterator it, Writer& range_writer) + { + hb_codepoint_t start_cp = 0, prev_run_start_cp = 0, run_start_cp = 0, end_cp = 0, last_gid = 0; + int run_length = 0 , delta = 0, prev_delta = 0; + + enum { + FIRST_SUB_RANGE, + FOLLOWING_SUB_RANGE, + } mode; + + while (it) { + // Start a new range + { + const auto& pair = *it; + start_cp = pair.first; + prev_run_start_cp = start_cp; + run_start_cp = start_cp; + end_cp = start_cp; + last_gid = pair.second; + run_length = 1; + prev_delta = 0; + } + + delta = last_gid - start_cp; + mode = FIRST_SUB_RANGE; + it++; + + while (it) { + // Process range + const auto& pair = *it; + hb_codepoint_t next_cp = pair.first; + hb_codepoint_t next_gid = pair.second; + if (next_cp != end_cp + 1) { + // Current range is over, stop processing. + break; + } + + if (next_gid == last_gid + 1) { + // The current run continues. + end_cp = next_cp; + run_length++; + last_gid = next_gid; + it++; + continue; + } + + // A new run is starting, decide if we want to commit the current run. + int split_cost = (mode == FIRST_SUB_RANGE) ? 8 : 16; + int run_cost = run_length * 2; + if (run_cost >= split_cost) { + commit_current_range(start_cp, + prev_run_start_cp, + run_start_cp, + end_cp, + delta, + prev_delta, + split_cost, + range_writer); + start_cp = next_cp; + } + + // Start the new run + mode = FOLLOWING_SUB_RANGE; + prev_run_start_cp = run_start_cp; + run_start_cp = next_cp; + end_cp = next_cp; + prev_delta = delta; + delta = next_gid - run_start_cp; + run_length = 1; + last_gid = next_gid; + it++; + } + + // Finalize range + commit_current_range (start_cp, + prev_run_start_cp, + run_start_cp, + end_cp, + delta, + prev_delta, + 8, + range_writer); + } + + if (likely (end_cp != 0xFFFF)) { + range_writer (0xFFFF, 0xFFFF, 1); + } + } + + /* + * Writes the current range as either one or two ranges depending on what is most efficient. + */ + template + void commit_current_range (hb_codepoint_t start, + hb_codepoint_t prev_run_start, + hb_codepoint_t run_start, + hb_codepoint_t end, + int run_delta, + int previous_run_delta, + int split_cost, + Writer& range_writer) { + bool should_split = false; + if (start < run_start && run_start < end) { + int run_cost = (end - run_start + 1) * 2; + if (run_cost >= split_cost) { + should_split = true; + } + } + + // TODO(grieger): handle case where delta is legitimately 0, mark range offset array instead? + if (should_split) { + if (start == prev_run_start) + range_writer (start, run_start - 1, previous_run_delta); + else + range_writer (start, run_start - 1, 0); + range_writer (run_start, end, run_delta); + return; + } + + + if (start == run_start) { + // Range is only a run + range_writer (start, end, run_delta); + return; + } + + // Write only a single non-run range. + range_writer (start, end, 0); + } + + template + unsigned serialize_find_segcount (Iterator it) { + struct Counter { + unsigned segcount = 0; + + void operator() (hb_codepoint_t start, + hb_codepoint_t end, + int delta) { + segcount++; + } + } counter; + + to_ranges (+it, counter); + return counter.segcount; + } + + + template + bool serialize_start_end_delta_arrays (hb_serialize_context_t *c, + Iterator it, + int segcount) + { + struct Writer { + hb_serialize_context_t *serializer_; + HBUINT16* end_code_; + HBUINT16* start_code_; + HBINT16* id_delta_; + int index_; + + Writer(hb_serialize_context_t *serializer) + : serializer_(serializer), + end_code_(nullptr), + start_code_(nullptr), + id_delta_(nullptr), + index_ (0) {} + void operator() (hb_codepoint_t start, + hb_codepoint_t end, + int delta) { + start_code_[index_] = start; + end_code_[index_] = end; + id_delta_[index_] = delta; + index_++; + } + } writer(c); + + writer.end_code_ = c->allocate_size (HBUINT16::static_size * segcount, false); + (void) c->allocate_size (2); // padding + writer.start_code_ = c->allocate_size (HBUINT16::static_size * segcount, false); + writer.id_delta_ = c->allocate_size (HBINT16::static_size * segcount, false); + + if (unlikely (!writer.end_code_ || !writer.start_code_ || !writer.id_delta_)) return false; + + to_ranges (+it, writer); + return true; + } + + template + HBUINT16* serialize_rangeoffset_glyid (hb_serialize_context_t *c, + Iterator it, + HBUINT16 *endCode, + HBUINT16 *startCode, + HBINT16 *idDelta, + unsigned segcount) + { + hb_map_t cp_to_gid { it }; + + HBUINT16 *idRangeOffset = c->allocate_size (HBUINT16::static_size * segcount); + if (unlikely (!c->check_success (idRangeOffset))) return nullptr; + if (unlikely ((char *)idRangeOffset - (char *)idDelta != (int) segcount * (int) HBINT16::static_size)) return nullptr; + + for (unsigned i : + hb_range (segcount) + | hb_filter ([&] (const unsigned _) { return idDelta[_] == 0; })) + { + idRangeOffset[i] = 2 * (c->start_embed () - idRangeOffset - i); + for (hb_codepoint_t cp = startCode[i]; cp <= endCode[i]; cp++) + { + HBUINT16 gid; + gid = cp_to_gid[cp]; + c->copy (gid); + } + } + + return idRangeOffset; + } + + template + void serialize (hb_serialize_context_t *c, + Iterator it) + { + auto format4_iter = + + it + | hb_filter ([&] (const hb_codepoint_pair_t _) + { return _.first <= 0xFFFF; }) + ; + + if (!format4_iter) return; + + unsigned table_initpos = c->length (); + if (unlikely (!c->extend_min (this))) return; + this->format = 4; + + hb_vector_t cp_to_gid { + format4_iter + }; + + //serialize endCode[], startCode[], idDelta[] + HBUINT16* endCode = c->start_embed (); + unsigned segcount = serialize_find_segcount (cp_to_gid.iter()); + if (unlikely (!serialize_start_end_delta_arrays (c, cp_to_gid.iter(), segcount))) + return; + + HBUINT16 *startCode = endCode + segcount + 1; + HBINT16 *idDelta = ((HBINT16*)startCode) + segcount; + + HBUINT16 *idRangeOffset = serialize_rangeoffset_glyid (c, + cp_to_gid.iter (), + endCode, + startCode, + idDelta, + segcount); + if (unlikely (!c->check_success (idRangeOffset))) return; + + this->length = c->length () - table_initpos; + if ((long long) this->length != (long long) c->length () - table_initpos) + { + // Length overflowed. Discard the current object before setting the error condition, otherwise + // discard is a noop which prevents the higher level code from reverting the serializer to the + // pre-error state in cmap4 overflow handling code. + c->pop_discard (); + c->err (HB_SERIALIZE_ERROR_INT_OVERFLOW); + return; + } + + this->segCountX2 = segcount * 2; + this->entrySelector = hb_max (1u, hb_bit_storage (segcount)) - 1; + this->searchRange = 2 * (1u << this->entrySelector); + this->rangeShift = segcount * 2 > this->searchRange + ? 2 * segcount - this->searchRange + : 0; + } + + unsigned get_language () const + { + return language; + } + struct accelerator_t { - inline void init (const CmapSubtableFormat4 *subtable) + accelerator_t () {} + accelerator_t (const CmapSubtableFormat4 *subtable) { init (subtable); } + + void init (const CmapSubtableFormat4 *subtable) { segCount = subtable->segCountX2 / 2; - endCount = subtable->values; + endCount = subtable->values.arrayZ; startCount = endCount + segCount + 1; idDelta = startCount + segCount; idRangeOffset = idDelta + segCount; @@ -82,68 +396,162 @@ struct CmapSubtableFormat4 glyphIdArrayLength = (subtable->length - 16 - 8 * segCount) / 2; } - static inline bool get_glyph_func (const void *obj, hb_codepoint_t codepoint, hb_codepoint_t *glyph) + bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const { - const accelerator_t *thiz = (const accelerator_t *) obj; - - /* Custom two-array bsearch. */ - int min = 0, max = (int) thiz->segCount - 1; - const UINT16 *startCount = thiz->startCount; - const UINT16 *endCount = thiz->endCount; - unsigned int i; - while (min <= max) + struct CustomRange { - int mid = (min + max) / 2; - if (codepoint < startCount[mid]) - max = mid - 1; - else if (codepoint > endCount[mid]) - min = mid + 1; - else + int cmp (hb_codepoint_t k, + unsigned distance) const { - i = mid; - goto found; + if (k > last) return +1; + if (k < (&last)[distance]/*first*/) return -1; + return 0; } - } - return false; + HBUINT16 last; + }; + + const HBUINT16 *found = hb_bsearch (codepoint, + this->endCount, + this->segCount, + sizeof (CustomRange), + _hb_cmp_method, + this->segCount + 1); + if (unlikely (!found)) + return false; + unsigned int i = found - endCount; - found: hb_codepoint_t gid; - unsigned int rangeOffset = thiz->idRangeOffset[i]; + unsigned int rangeOffset = this->idRangeOffset[i]; if (rangeOffset == 0) - gid = codepoint + thiz->idDelta[i]; + gid = codepoint + this->idDelta[i]; else { /* Somebody has been smoking... */ - unsigned int index = rangeOffset / 2 + (codepoint - thiz->startCount[i]) + i - thiz->segCount; - if (unlikely (index >= thiz->glyphIdArrayLength)) + unsigned int index = rangeOffset / 2 + (codepoint - this->startCount[i]) + i - this->segCount; + if (unlikely (index >= this->glyphIdArrayLength)) return false; - gid = thiz->glyphIdArray[index]; + gid = this->glyphIdArray[index]; if (unlikely (!gid)) return false; - gid += thiz->idDelta[i]; + gid += this->idDelta[i]; } - - *glyph = gid & 0xFFFFu; + gid &= 0xFFFFu; + if (unlikely (!gid)) + return false; + *glyph = gid; return true; } - const UINT16 *endCount; - const UINT16 *startCount; - const UINT16 *idDelta; - const UINT16 *idRangeOffset; - const UINT16 *glyphIdArray; + HB_INTERNAL static bool get_glyph_func (const void *obj, hb_codepoint_t codepoint, hb_codepoint_t *glyph) + { return ((const accelerator_t *) obj)->get_glyph (codepoint, glyph); } + + void collect_unicodes (hb_set_t *out) const + { + unsigned int count = this->segCount; + if (count && this->startCount[count - 1] == 0xFFFFu) + count--; /* Skip sentinel segment. */ + for (unsigned int i = 0; i < count; i++) + { + hb_codepoint_t start = this->startCount[i]; + hb_codepoint_t end = this->endCount[i]; + unsigned int rangeOffset = this->idRangeOffset[i]; + out->add_range(start, end); + if (rangeOffset == 0) + { + for (hb_codepoint_t codepoint = start; codepoint <= end; codepoint++) + { + hb_codepoint_t gid = (codepoint + this->idDelta[i]) & 0xFFFFu; + if (unlikely (!gid)) + out->del(codepoint); + } + } + else + { + for (hb_codepoint_t codepoint = start; codepoint <= end; codepoint++) + { + unsigned int index = rangeOffset / 2 + (codepoint - this->startCount[i]) + i - this->segCount; + if (unlikely (index >= this->glyphIdArrayLength)) + { + out->del_range (codepoint, end); + break; + } + hb_codepoint_t gid = this->glyphIdArray[index]; + if (unlikely (!gid)) + out->del(codepoint); + } + } + } + } + + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const + { + // TODO(grieger): optimize similar to collect_unicodes + // (ie. use add_range()) + unsigned count = this->segCount; + if (count && this->startCount[count - 1] == 0xFFFFu) + count--; /* Skip sentinel segment. */ + for (unsigned i = 0; i < count; i++) + { + hb_codepoint_t start = this->startCount[i]; + hb_codepoint_t end = this->endCount[i]; + unsigned rangeOffset = this->idRangeOffset[i]; + if (rangeOffset == 0) + { + for (hb_codepoint_t codepoint = start; codepoint <= end; codepoint++) + { + hb_codepoint_t gid = (codepoint + this->idDelta[i]) & 0xFFFFu; + if (unlikely (!gid)) + continue; + unicodes->add (codepoint); + mapping->set (codepoint, gid); + } + } + else + { + for (hb_codepoint_t codepoint = start; codepoint <= end; codepoint++) + { + unsigned index = rangeOffset / 2 + (codepoint - this->startCount[i]) + i - this->segCount; + if (unlikely (index >= this->glyphIdArrayLength)) + break; + hb_codepoint_t gid = this->glyphIdArray[index]; + if (unlikely (!gid)) + continue; + unicodes->add (codepoint); + mapping->set (codepoint, gid); + } + } + } + } + + const HBUINT16 *endCount; + const HBUINT16 *startCount; + const HBUINT16 *idDelta; + const HBUINT16 *idRangeOffset; + const HBUINT16 *glyphIdArray; unsigned int segCount; unsigned int glyphIdArrayLength; }; - inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const + bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const { - accelerator_t accel; - accel.init (this); + accelerator_t accel (this); return accel.get_glyph_func (&accel, codepoint, glyph); } + void collect_unicodes (hb_set_t *out) const + { + accelerator_t accel (this); + accel.collect_unicodes (out); + } - inline bool sanitize (hb_sanitize_context_t *c) const + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const + { + accelerator_t accel (this); + accel.collect_mapping (unicodes, mapping); + } + + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); if (unlikely (!c->check_struct (this))) @@ -154,9 +562,9 @@ struct CmapSubtableFormat4 /* Some broken fonts have too long of a "length" value. * If that is the case, just change the value to truncate * the subtable at the end of the blob. */ - uint16_t new_length = (uint16_t) MIN ((uintptr_t) 65535, - (uintptr_t) (c->end - - (char *) this)); + uint16_t new_length = (uint16_t) hb_min ((uintptr_t) 65535, + (uintptr_t) (c->end - + (char *) this)); if (!c->try_set (&length, new_length)) return_trace (false); } @@ -164,25 +572,29 @@ struct CmapSubtableFormat4 return_trace (16 + 4 * (unsigned int) segCountX2 <= length); } - protected: - UINT16 format; /* Format number is set to 4. */ - UINT16 length; /* This is the length in bytes of the - * subtable. */ - UINT16 languageZ; /* Ignore. */ - UINT16 segCountX2; /* 2 x segCount. */ - UINT16 searchRangeZ; /* 2 * (2**floor(log2(segCount))) */ - UINT16 entrySelectorZ; /* log2(searchRange/2) */ - UINT16 rangeShiftZ; /* 2 x segCount - searchRange */ - UINT16 values[VAR]; + + protected: + HBUINT16 format; /* Format number is set to 4. */ + HBUINT16 length; /* This is the length in bytes of the + * subtable. */ + HBUINT16 language; /* Ignore. */ + HBUINT16 segCountX2; /* 2 x segCount. */ + HBUINT16 searchRange; /* 2 * (2**floor(log2(segCount))) */ + HBUINT16 entrySelector; /* log2(searchRange/2) */ + HBUINT16 rangeShift; /* 2 x segCount - searchRange */ + + UnsizedArrayOf + values; #if 0 - UINT16 endCount[segCount]; /* End characterCode for each segment, + HBUINT16 endCount[segCount]; /* End characterCode for each segment, * last=0xFFFFu. */ - UINT16 reservedPad; /* Set to 0. */ - UINT16 startCount[segCount]; /* Start character code for each segment. */ - INT16 idDelta[segCount]; /* Delta for all character codes in segment. */ - UINT16 idRangeOffset[segCount];/* Offsets into glyphIdArray or 0 */ - UINT16 glyphIdArray[VAR]; /* Glyph index array (arbitrary length) */ + HBUINT16 reservedPad; /* Set to 0. */ + HBUINT16 startCount[segCount]; /* Start character code for each segment. */ + HBINT16 idDelta[segCount]; /* Delta for all character codes in segment. */ + HBUINT16 idRangeOffset[segCount];/* Offsets into glyphIdArray or 0 */ + UnsizedArrayOf + glyphIdArray; /* Glyph index array (arbitrary length) */ #endif public: @@ -193,6 +605,9 @@ struct CmapSubtableLongGroup { friend struct CmapSubtableFormat12; friend struct CmapSubtableFormat13; + template + friend struct CmapSubtableLongSegmented; + friend struct cmap; int cmp (hb_codepoint_t codepoint) const { @@ -201,35 +616,65 @@ struct CmapSubtableLongGroup return 0; } - inline bool sanitize (hb_sanitize_context_t *c) const + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this)); } private: - UINT32 startCharCode; /* First character code in this group. */ - UINT32 endCharCode; /* Last character code in this group. */ - UINT32 glyphID; /* Glyph index; interpretation depends on - * subtable format. */ + HBUINT32 startCharCode; /* First character code in this group. */ + HBUINT32 endCharCode; /* Last character code in this group. */ + HBUINT32 glyphID; /* Glyph index; interpretation depends on + * subtable format. */ public: DEFINE_SIZE_STATIC (12); }; +DECLARE_NULL_NAMESPACE_BYTES (OT, CmapSubtableLongGroup); template struct CmapSubtableTrimmed { - inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const + bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const { /* Rely on our implicit array bound-checking. */ hb_codepoint_t gid = glyphIdArray[codepoint - startCharCode]; - if (!gid) + if (unlikely (!gid)) return false; *glyph = gid; return true; } - inline bool sanitize (hb_sanitize_context_t *c) const + unsigned get_language () const + { + return language; + } + + void collect_unicodes (hb_set_t *out) const + { + hb_codepoint_t start = startCharCode; + unsigned int count = glyphIdArray.len; + for (unsigned int i = 0; i < count; i++) + if (glyphIdArray[i]) + out->add (start + i); + } + + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const + { + hb_codepoint_t start_cp = startCharCode; + unsigned count = glyphIdArray.len; + for (unsigned i = 0; i < count; i++) + if (glyphIdArray[i]) + { + hb_codepoint_t unicode = start_cp + i; + hb_codepoint_t glyphid = glyphIdArray[i]; + unicodes->add (unicode); + mapping->set (unicode, glyphid); + } + } + + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && glyphIdArray.sanitize (c)); @@ -237,43 +682,112 @@ struct CmapSubtableTrimmed protected: UINT formatReserved; /* Subtable format and (maybe) padding. */ - UINT lengthZ; /* Byte length of this subtable. */ - UINT languageZ; /* Ignore. */ + UINT length; /* Byte length of this subtable. */ + UINT language; /* Ignore. */ UINT startCharCode; /* First character code covered. */ - ArrayOf + ArrayOf glyphIdArray; /* Array of glyph index values for character * codes in the range. */ public: DEFINE_SIZE_ARRAY (5 * sizeof (UINT), glyphIdArray); }; -struct CmapSubtableFormat6 : CmapSubtableTrimmed {}; -struct CmapSubtableFormat10 : CmapSubtableTrimmed {}; +struct CmapSubtableFormat6 : CmapSubtableTrimmed {}; +struct CmapSubtableFormat10 : CmapSubtableTrimmed {}; template struct CmapSubtableLongSegmented { - inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const + friend struct cmap; + + bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const { - int i = groups.bsearch (codepoint); - if (i == -1) + hb_codepoint_t gid = T::group_get_glyph (groups.bsearch (codepoint), codepoint); + if (unlikely (!gid)) return false; - *glyph = T::group_get_glyph (groups[i], codepoint); + *glyph = gid; return true; } - inline bool sanitize (hb_sanitize_context_t *c) const + unsigned get_language () const + { + return language; + } + + void collect_unicodes (hb_set_t *out, unsigned int num_glyphs) const + { + for (unsigned int i = 0; i < this->groups.len; i++) + { + hb_codepoint_t start = this->groups[i].startCharCode; + hb_codepoint_t end = hb_min ((hb_codepoint_t) this->groups[i].endCharCode, + (hb_codepoint_t) HB_UNICODE_MAX); + hb_codepoint_t gid = this->groups[i].glyphID; + if (!gid) + { + /* Intention is: if (hb_is_same (T, CmapSubtableFormat13)) continue; */ + if (! T::group_get_glyph (this->groups[i], end)) continue; + start++; + gid++; + } + if (unlikely ((unsigned int) gid >= num_glyphs)) continue; + if (unlikely ((unsigned int) (gid + end - start) >= num_glyphs)) + end = start + (hb_codepoint_t) num_glyphs - gid; + + out->add_range (start, hb_min (end, 0x10FFFFu)); + } + } + + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping, /* OUT */ + unsigned num_glyphs) const + { + hb_codepoint_t last_end = 0; + for (unsigned i = 0; i < this->groups.len; i++) + { + hb_codepoint_t start = this->groups[i].startCharCode; + hb_codepoint_t end = hb_min ((hb_codepoint_t) this->groups[i].endCharCode, + (hb_codepoint_t) HB_UNICODE_MAX); + if (unlikely (start > end || start < last_end)) { + // Range is not in order and is invalid, skip it. + continue; + } + last_end = end; + + + hb_codepoint_t gid = this->groups[i].glyphID; + if (!gid) + { + if (T::formatNumber == 13) continue; + start++; + gid++; + } + if (unlikely ((unsigned int) gid >= num_glyphs)) continue; + if (unlikely ((unsigned int) (gid + end - start) >= num_glyphs)) + end = start + (hb_codepoint_t) num_glyphs - gid; + + mapping->alloc (mapping->get_population () + end - start + 1); + + for (unsigned cp = start; cp <= end; cp++) + { + unicodes->add (cp); + mapping->set (cp, gid); + gid += T::increment; + } + } + } + + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && groups.sanitize (c)); } protected: - UINT16 format; /* Subtable format; set to 12. */ - UINT16 reservedZ; /* Reserved; set to 0. */ - UINT32 lengthZ; /* Byte length of this subtable. */ - UINT32 languageZ; /* Ignore. */ - SortedArrayOf + HBUINT16 format; /* Subtable format; set to 12. */ + HBUINT16 reserved; /* Reserved; set to 0. */ + HBUINT32 length; /* Byte length of this subtable. */ + HBUINT32 language; /* Ignore. */ + SortedArray32Of groups; /* Groupings. */ public: DEFINE_SIZE_ARRAY (16, groups); @@ -281,15 +795,86 @@ struct CmapSubtableLongSegmented struct CmapSubtableFormat12 : CmapSubtableLongSegmented { - static inline hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group, - hb_codepoint_t u) - { return group.glyphID + (u - group.startCharCode); } + static constexpr int increment = 1; + static constexpr int formatNumber = 12; + + static hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group, + hb_codepoint_t u) + { return likely (group.startCharCode <= group.endCharCode) ? + group.glyphID + (u - group.startCharCode) : 0; } + + + template + void serialize (hb_serialize_context_t *c, + Iterator it) + { + if (!it) return; + unsigned table_initpos = c->length (); + if (unlikely (!c->extend_min (this))) return; + + hb_codepoint_t startCharCode = (hb_codepoint_t) -1, endCharCode = (hb_codepoint_t) -1; + hb_codepoint_t glyphID = 0; + + for (const auto& _ : +it) + { + if (startCharCode == (hb_codepoint_t) -1) + { + startCharCode = _.first; + endCharCode = _.first; + glyphID = _.second; + } + else if (!_is_gid_consecutive (endCharCode, startCharCode, glyphID, _.first, _.second)) + { + CmapSubtableLongGroup grouprecord; + grouprecord.startCharCode = startCharCode; + grouprecord.endCharCode = endCharCode; + grouprecord.glyphID = glyphID; + c->copy (grouprecord); + + startCharCode = _.first; + endCharCode = _.first; + glyphID = _.second; + } + else + endCharCode = _.first; + } + + CmapSubtableLongGroup record; + record.startCharCode = startCharCode; + record.endCharCode = endCharCode; + record.glyphID = glyphID; + c->copy (record); + + this->format = 12; + this->reserved = 0; + this->length = c->length () - table_initpos; + this->groups.len = (this->length - min_size) / CmapSubtableLongGroup::static_size; + } + + static size_t get_sub_table_size (const hb_sorted_vector_t &groups_data) + { return 16 + 12 * groups_data.length; } + + private: + static bool _is_gid_consecutive (hb_codepoint_t endCharCode, + hb_codepoint_t startCharCode, + hb_codepoint_t glyphID, + hb_codepoint_t cp, + hb_codepoint_t new_gid) + { + return (cp - 1 == endCharCode) && + new_gid == glyphID + (cp - startCharCode); + } + }; struct CmapSubtableFormat13 : CmapSubtableLongSegmented { - static inline hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group, - hb_codepoint_t u HB_UNUSED) + static constexpr int increment = 0; + static constexpr int formatNumber = 13; + + static hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group, + hb_codepoint_t u HB_UNUSED) { return group.glyphID; } }; @@ -302,76 +887,270 @@ typedef enum struct UnicodeValueRange { - inline int cmp (const hb_codepoint_t &codepoint) const + int cmp (const hb_codepoint_t &codepoint) const { if (codepoint < startUnicodeValue) return -1; if (codepoint > startUnicodeValue + additionalCount) return +1; return 0; } - inline bool sanitize (hb_sanitize_context_t *c) const + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this)); } - UINT24 startUnicodeValue; /* First value in this range. */ - UINT8 additionalCount; /* Number of additional values in this + HBUINT24 startUnicodeValue; /* First value in this range. */ + HBUINT8 additionalCount; /* Number of additional values in this * range. */ public: DEFINE_SIZE_STATIC (4); }; -typedef SortedArrayOf DefaultUVS; +struct DefaultUVS : SortedArray32Of +{ + void collect_unicodes (hb_set_t *out) const + { + unsigned int count = len; + for (unsigned int i = 0; i < count; i++) + { + hb_codepoint_t first = arrayZ[i].startUnicodeValue; + hb_codepoint_t last = hb_min ((hb_codepoint_t) (first + arrayZ[i].additionalCount), + (hb_codepoint_t) HB_UNICODE_MAX); + out->add_range (first, last); + } + } + + DefaultUVS* copy (hb_serialize_context_t *c, + const hb_set_t *unicodes) const + { + auto *out = c->start_embed (); + auto snap = c->snapshot (); + + HBUINT32 len; + len = 0; + if (unlikely (!c->copy (len))) return nullptr; + unsigned init_len = c->length (); + + if (this->len > unicodes->get_population () * hb_bit_storage ((unsigned) this->len)) + { + hb_codepoint_t start = HB_SET_VALUE_INVALID; + hb_codepoint_t end = HB_SET_VALUE_INVALID; + + for (auto u : *unicodes) + { + if (!as_array ().bsearch (u)) + continue; + if (start == HB_SET_VALUE_INVALID) + { + start = u; + end = start - 1; + } + if (end + 1 != u || end - start == 255) + { + UnicodeValueRange rec; + rec.startUnicodeValue = start; + rec.additionalCount = end - start; + c->copy (rec); + start = u; + } + end = u; + } + if (start != HB_SET_VALUE_INVALID) + { + UnicodeValueRange rec; + rec.startUnicodeValue = start; + rec.additionalCount = end - start; + c->copy (rec); + } + + } + else + { + hb_codepoint_t lastCode = HB_SET_VALUE_INVALID; + int count = -1; + + for (const UnicodeValueRange& _ : *this) + { + hb_codepoint_t curEntry = (hb_codepoint_t) (_.startUnicodeValue - 1); + hb_codepoint_t end = curEntry + _.additionalCount + 2; + + for (; unicodes->next (&curEntry) && curEntry < end;) + { + count += 1; + if (lastCode == HB_SET_VALUE_INVALID) + lastCode = curEntry; + else if (lastCode + count != curEntry) + { + UnicodeValueRange rec; + rec.startUnicodeValue = lastCode; + rec.additionalCount = count - 1; + c->copy (rec); + + lastCode = curEntry; + count = 0; + } + } + } + + if (lastCode != HB_MAP_VALUE_INVALID) + { + UnicodeValueRange rec; + rec.startUnicodeValue = lastCode; + rec.additionalCount = count; + c->copy (rec); + } + } + + if (c->length () - init_len == 0) + { + c->revert (snap); + return nullptr; + } + else + { + if (unlikely (!c->check_assign (out->len, + (c->length () - init_len) / UnicodeValueRange::static_size, + HB_SERIALIZE_ERROR_INT_OVERFLOW))) return nullptr; + return out; + } + } + + public: + DEFINE_SIZE_ARRAY (4, *this); +}; struct UVSMapping { - inline int cmp (const hb_codepoint_t &codepoint) const - { - return unicodeValue.cmp (codepoint); - } + int cmp (const hb_codepoint_t &codepoint) const + { return unicodeValue.cmp (codepoint); } - inline bool sanitize (hb_sanitize_context_t *c) const + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this)); } - UINT24 unicodeValue; /* Base Unicode value of the UVS */ - GlyphID glyphID; /* Glyph ID of the UVS */ + HBUINT24 unicodeValue; /* Base Unicode value of the UVS */ + HBGlyphID16 glyphID; /* Glyph ID of the UVS */ public: DEFINE_SIZE_STATIC (5); }; -typedef SortedArrayOf NonDefaultUVS; +struct NonDefaultUVS : SortedArray32Of +{ + void collect_unicodes (hb_set_t *out) const + { + for (const auto& a : as_array ()) + out->add (a.unicodeValue); + } + + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const + { + for (const auto& a : as_array ()) + { + hb_codepoint_t unicode = a.unicodeValue; + hb_codepoint_t glyphid = a.glyphID; + unicodes->add (unicode); + mapping->set (unicode, glyphid); + } + } + + void closure_glyphs (const hb_set_t *unicodes, + hb_set_t *glyphset) const + { + + as_array () + | hb_filter (unicodes, &UVSMapping::unicodeValue) + | hb_map (&UVSMapping::glyphID) + | hb_sink (glyphset) + ; + } + + NonDefaultUVS* copy (hb_serialize_context_t *c, + const hb_set_t *unicodes, + const hb_set_t *glyphs_requested, + const hb_map_t *glyph_map) const + { + auto *out = c->start_embed (); + auto it = + + as_array () + | hb_filter ([&] (const UVSMapping& _) + { + return unicodes->has (_.unicodeValue) || glyphs_requested->has (_.glyphID); + }) + ; + + if (!it) return nullptr; + + HBUINT32 len; + len = it.len (); + if (unlikely (!c->copy (len))) return nullptr; + + for (const UVSMapping& _ : it) + { + UVSMapping mapping; + mapping.unicodeValue = _.unicodeValue; + mapping.glyphID = glyph_map->get (_.glyphID); + c->copy (mapping); + } + + return out; + } + + public: + DEFINE_SIZE_ARRAY (4, *this); +}; struct VariationSelectorRecord { - inline glyph_variant_t get_glyph (hb_codepoint_t codepoint, - hb_codepoint_t *glyph, - const void *base) const + glyph_variant_t get_glyph (hb_codepoint_t codepoint, + hb_codepoint_t *glyph, + const void *base) const { - int i; - const DefaultUVS &defaults = base+defaultUVS; - i = defaults.bsearch (codepoint); - if (i != -1) + if ((base+defaultUVS).bfind (codepoint)) return GLYPH_VARIANT_USE_DEFAULT; - const NonDefaultUVS &nonDefaults = base+nonDefaultUVS; - i = nonDefaults.bsearch (codepoint); - if (i != -1) + const UVSMapping &nonDefault = (base+nonDefaultUVS).bsearch (codepoint); + if (nonDefault.glyphID) { - *glyph = nonDefaults[i].glyphID; + *glyph = nonDefault.glyphID; return GLYPH_VARIANT_FOUND; } return GLYPH_VARIANT_NOT_FOUND; } - inline int cmp (const hb_codepoint_t &variation_selector) const + VariationSelectorRecord(const VariationSelectorRecord& other) { - return varSelector.cmp (variation_selector); + *this = other; } - inline bool sanitize (hb_sanitize_context_t *c, const void *base) const + void operator= (const VariationSelectorRecord& other) + { + varSelector = other.varSelector; + HBUINT32 offset = other.defaultUVS; + defaultUVS = offset; + offset = other.nonDefaultUVS; + nonDefaultUVS = offset; + } + + void collect_unicodes (hb_set_t *out, const void *base) const + { + (base+defaultUVS).collect_unicodes (out); + (base+nonDefaultUVS).collect_unicodes (out); + } + + void collect_mapping (const void *base, + hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const + { + (base+defaultUVS).collect_unicodes (unicodes); + (base+nonDefaultUVS).collect_mapping (unicodes, mapping); + } + + int cmp (const hb_codepoint_t &variation_selector) const + { return varSelector.cmp (variation_selector); } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && @@ -379,25 +1158,177 @@ struct VariationSelectorRecord nonDefaultUVS.sanitize (c, base)); } - UINT24 varSelector; /* Variation selector. */ - LOffsetTo - defaultUVS; /* Offset to Default UVS Table. May be 0. */ - LOffsetTo - nonDefaultUVS; /* Offset to Non-Default UVS Table. May be 0. */ + hb_pair_t + copy (hb_serialize_context_t *c, + const hb_set_t *unicodes, + const hb_set_t *glyphs_requested, + const hb_map_t *glyph_map, + const void *base) const + { + auto snap = c->snapshot (); + auto *out = c->embed (*this); + if (unlikely (!out)) return hb_pair (0, 0); + + out->defaultUVS = 0; + out->nonDefaultUVS = 0; + + unsigned non_default_uvs_objidx = 0; + if (nonDefaultUVS != 0) + { + c->push (); + if (c->copy (base+nonDefaultUVS, unicodes, glyphs_requested, glyph_map)) + non_default_uvs_objidx = c->pop_pack (); + else c->pop_discard (); + } + + unsigned default_uvs_objidx = 0; + if (defaultUVS != 0) + { + c->push (); + if (c->copy (base+defaultUVS, unicodes)) + default_uvs_objidx = c->pop_pack (); + else c->pop_discard (); + } + + + if (!default_uvs_objidx && !non_default_uvs_objidx) + c->revert (snap); + + return hb_pair (default_uvs_objidx, non_default_uvs_objidx); + } + + HBUINT24 varSelector; /* Variation selector. */ + Offset32To + defaultUVS; /* Offset to Default UVS Table. May be 0. */ + Offset32To + nonDefaultUVS; /* Offset to Non-Default UVS Table. May be 0. */ public: DEFINE_SIZE_STATIC (11); }; struct CmapSubtableFormat14 { - inline glyph_variant_t get_glyph_variant (hb_codepoint_t codepoint, - hb_codepoint_t variation_selector, - hb_codepoint_t *glyph) const + glyph_variant_t get_glyph_variant (hb_codepoint_t codepoint, + hb_codepoint_t variation_selector, + hb_codepoint_t *glyph) const + { return record.bsearch (variation_selector).get_glyph (codepoint, glyph, this); } + + void collect_variation_selectors (hb_set_t *out) const { - return record[record.bsearch(variation_selector)].get_glyph (codepoint, glyph, this); + for (const auto& a : record.as_array ()) + out->add (a.varSelector); + } + void collect_variation_unicodes (hb_codepoint_t variation_selector, + hb_set_t *out) const + { record.bsearch (variation_selector).collect_unicodes (out, this); } + + void serialize (hb_serialize_context_t *c, + const hb_set_t *unicodes, + const hb_set_t *glyphs_requested, + const hb_map_t *glyph_map, + const void *base) + { + auto snap = c->snapshot (); + unsigned table_initpos = c->length (); + const char* init_tail = c->tail; + + if (unlikely (!c->extend_min (this))) return; + this->format = 14; + + auto src_tbl = reinterpret_cast (base); + + /* + * Some versions of OTS require that offsets are in order. Due to the use + * of push()/pop_pack() serializing the variation records in order results + * in the offsets being in reverse order (first record has the largest + * offset). While this is perfectly valid, it will cause some versions of + * OTS to consider this table bad. + * + * So to prevent this issue we serialize the variation records in reverse + * order, so that the offsets are ordered from small to large. Since + * variation records are supposed to be in increasing order of varSelector + * we then have to reverse the order of the written variation selector + * records after everything is finalized. + */ + hb_vector_t> obj_indices; + for (int i = src_tbl->record.len - 1; i >= 0; i--) + { + hb_pair_t result = src_tbl->record[i].copy (c, unicodes, glyphs_requested, glyph_map, base); + if (result.first || result.second) + obj_indices.push (result); + } + + if (c->length () - table_initpos == CmapSubtableFormat14::min_size) + { + c->revert (snap); + return; + } + + if (unlikely (!c->check_success (!obj_indices.in_error ()))) + return; + + int tail_len = init_tail - c->tail; + c->check_assign (this->length, c->length () - table_initpos + tail_len, + HB_SERIALIZE_ERROR_INT_OVERFLOW); + c->check_assign (this->record.len, + (c->length () - table_initpos - CmapSubtableFormat14::min_size) / + VariationSelectorRecord::static_size, + HB_SERIALIZE_ERROR_INT_OVERFLOW); + + /* Correct the incorrect write order by reversing the order of the variation + records array. */ + _reverse_variation_records (); + + /* Now that records are in the right order, we can set up the offsets. */ + _add_links_to_variation_records (c, obj_indices); } - inline bool sanitize (hb_sanitize_context_t *c) const + void _reverse_variation_records () + { + record.as_array ().reverse (); + } + + void _add_links_to_variation_records (hb_serialize_context_t *c, + const hb_vector_t>& obj_indices) + { + for (unsigned i = 0; i < obj_indices.length; i++) + { + /* + * Since the record array has been reversed (see comments in copy()) + * but obj_indices has not been, the indices at obj_indices[i] + * are for the variation record at record[j]. + */ + int j = obj_indices.length - 1 - i; + c->add_link (record[j].defaultUVS, obj_indices[i].first); + c->add_link (record[j].nonDefaultUVS, obj_indices[i].second); + } + } + + void closure_glyphs (const hb_set_t *unicodes, + hb_set_t *glyphset) const + { + + hb_iter (record) + | hb_filter (hb_bool, &VariationSelectorRecord::nonDefaultUVS) + | hb_map (&VariationSelectorRecord::nonDefaultUVS) + | hb_map (hb_add (this)) + | hb_apply ([=] (const NonDefaultUVS& _) { _.closure_glyphs (unicodes, glyphset); }) + ; + } + + void collect_unicodes (hb_set_t *out) const + { + for (const VariationSelectorRecord& _ : record) + _.collect_unicodes (out, this); + } + + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const + { + for (const VariationSelectorRecord& _ : record) + _.collect_mapping (this, unicodes, mapping); + } + + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && @@ -405,9 +1336,9 @@ struct CmapSubtableFormat14 } protected: - UINT16 format; /* Format number is set to 14. */ - UINT32 lengthZ; /* Byte length of this subtable. */ - SortedArrayOf + HBUINT16 format; /* Format number is set to 14. */ + HBUINT32 length; /* Byte length of this subtable. */ + SortedArray32Of record; /* Variation selector records; sorted * in increasing order of `varSelector'. */ public: @@ -418,22 +1349,81 @@ struct CmapSubtable { /* Note: We intentionally do NOT implement subtable formats 2 and 8. */ - inline bool get_glyph (hb_codepoint_t codepoint, - hb_codepoint_t *glyph) const + bool get_glyph (hb_codepoint_t codepoint, + hb_codepoint_t *glyph) const { switch (u.format) { - case 0: return u.format0 .get_glyph(codepoint, glyph); - case 4: return u.format4 .get_glyph(codepoint, glyph); - case 6: return u.format6 .get_glyph(codepoint, glyph); - case 10: return u.format10.get_glyph(codepoint, glyph); - case 12: return u.format12.get_glyph(codepoint, glyph); - case 13: return u.format13.get_glyph(codepoint, glyph); + case 0: return u.format0 .get_glyph (codepoint, glyph); + case 4: return u.format4 .get_glyph (codepoint, glyph); + case 6: return u.format6 .get_glyph (codepoint, glyph); + case 10: return u.format10.get_glyph (codepoint, glyph); + case 12: return u.format12.get_glyph (codepoint, glyph); + case 13: return u.format13.get_glyph (codepoint, glyph); case 14: default: return false; } } + void collect_unicodes (hb_set_t *out, unsigned int num_glyphs = UINT_MAX) const + { + switch (u.format) { + case 0: u.format0 .collect_unicodes (out); return; + case 4: u.format4 .collect_unicodes (out); return; + case 6: u.format6 .collect_unicodes (out); return; + case 10: u.format10.collect_unicodes (out); return; + case 12: u.format12.collect_unicodes (out, num_glyphs); return; + case 13: u.format13.collect_unicodes (out, num_glyphs); return; + case 14: + default: return; + } + } - inline bool sanitize (hb_sanitize_context_t *c) const + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping, /* OUT */ + unsigned num_glyphs = UINT_MAX) const + { + switch (u.format) { + case 0: u.format0 .collect_mapping (unicodes, mapping); return; + case 4: u.format4 .collect_mapping (unicodes, mapping); return; + case 6: u.format6 .collect_mapping (unicodes, mapping); return; + case 10: u.format10.collect_mapping (unicodes, mapping); return; + case 12: u.format12.collect_mapping (unicodes, mapping, num_glyphs); return; + case 13: u.format13.collect_mapping (unicodes, mapping, num_glyphs); return; + case 14: + default: return; + } + } + + unsigned get_language () const + { + switch (u.format) { + case 0: return u.format0 .get_language (); + case 4: return u.format4 .get_language (); + case 6: return u.format6 .get_language (); + case 10: return u.format10.get_language (); + case 12: return u.format12.get_language (); + case 13: return u.format13.get_language (); + case 14: + default: return 0; + } + } + + template + void serialize (hb_serialize_context_t *c, + Iterator it, + unsigned format, + const hb_subset_plan_t *plan, + const void *base) + { + switch (format) { + case 4: return u.format4.serialize (c, it); + case 12: return u.format12.serialize (c, it); + case 14: return u.format14.serialize (c, &plan->unicodes, &plan->glyphs_requested, plan->glyph_map, base); + default: return; + } + } + + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); if (!u.format.sanitize (c)) return_trace (false); @@ -451,7 +1441,7 @@ struct CmapSubtable public: union { - UINT16 format; /* Format identifier */ + HBUINT16 format; /* Format identifier */ CmapSubtableFormat0 format0; CmapSubtableFormat4 format4; CmapSubtableFormat6 format6; @@ -467,7 +1457,7 @@ struct CmapSubtable struct EncodingRecord { - inline int cmp (const EncodingRecord &other) const + int cmp (const EncodingRecord &other) const { int ret; ret = platformID.cmp (other.platformID); @@ -477,26 +1467,596 @@ struct EncodingRecord return 0; } - inline bool sanitize (hb_sanitize_context_t *c, const void *base) const + bool sanitize (hb_sanitize_context_t *c, const void *base) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && subtable.sanitize (c, base)); } - UINT16 platformID; /* Platform ID. */ - UINT16 encodingID; /* Platform-specific encoding ID. */ - LOffsetTo + template + EncodingRecord* copy (hb_serialize_context_t *c, + Iterator it, + unsigned format, + const void *base, + const hb_subset_plan_t *plan, + /* INOUT */ unsigned *objidx) const + { + TRACE_SERIALIZE (this); + auto snap = c->snapshot (); + auto *out = c->embed (this); + if (unlikely (!out)) return_trace (nullptr); + out->subtable = 0; + + if (*objidx == 0) + { + CmapSubtable *cmapsubtable = c->push (); + unsigned origin_length = c->length (); + cmapsubtable->serialize (c, it, format, plan, &(base+subtable)); + if (c->length () - origin_length > 0) *objidx = c->pop_pack (); + else c->pop_discard (); + } + + if (*objidx == 0) + { + c->revert (snap); + return_trace (nullptr); + } + + c->add_link (out->subtable, *objidx); + return_trace (out); + } + + HBUINT16 platformID; /* Platform ID. */ + HBUINT16 encodingID; /* Platform-specific encoding ID. */ + Offset32To subtable; /* Byte offset from beginning of table to the subtable for this encoding. */ public: DEFINE_SIZE_STATIC (8); }; +struct cmap; + +struct SubtableUnicodesCache { + + private: + hb_blob_ptr_t base_blob; + const char* base; + hb_hashmap_t> cached_unicodes; + + public: + + static SubtableUnicodesCache* create (hb_blob_ptr_t source_table) + { + SubtableUnicodesCache* cache = + (SubtableUnicodesCache*) hb_malloc (sizeof(SubtableUnicodesCache)); + new (cache) SubtableUnicodesCache (source_table); + return cache; + } + + static void destroy (void* value) { + if (!value) return; + + SubtableUnicodesCache* cache = (SubtableUnicodesCache*) value; + cache->~SubtableUnicodesCache (); + hb_free (cache); + } + + SubtableUnicodesCache(const void* cmap_base) + : base_blob(), + base ((const char*) cmap_base), + cached_unicodes () + {} + + SubtableUnicodesCache(hb_blob_ptr_t base_blob_) + : base_blob(base_blob_), + base ((const char *) base_blob.get()), + cached_unicodes () + {} + + ~SubtableUnicodesCache() + { + base_blob.destroy (); + } + + bool same_base(const void* other) const + { + return other == (const void*) base; + } + + const hb_set_t* set_for (const EncodingRecord* record, + SubtableUnicodesCache& mutable_cache) const + { + if (cached_unicodes.has ((unsigned) ((const char *) record - base))) + return cached_unicodes.get ((unsigned) ((const char *) record - base)); + + return mutable_cache.set_for (record); + } + + const hb_set_t* set_for (const EncodingRecord* record) + { + if (!cached_unicodes.has ((unsigned) ((const char *) record - base))) + { + hb_set_t *s = hb_set_create (); + if (unlikely (s->in_error ())) + return hb_set_get_empty (); + + (base+record->subtable).collect_unicodes (s); + + if (unlikely (!cached_unicodes.set ((unsigned) ((const char *) record - base), hb::unique_ptr {s}))) + return hb_set_get_empty (); + + return s; + } + return cached_unicodes.get ((unsigned) ((const char *) record - base)); + } + +}; + +static inline uint_fast16_t +_hb_symbol_pua_map (unsigned codepoint) +{ + if (codepoint <= 0x00FFu) + { + /* For symbol-encoded OpenType fonts, we duplicate the + * U+F000..F0FF range at U+0000..U+00FF. That's what + * Windows seems to do, and that's hinted about at: + * https://docs.microsoft.com/en-us/typography/opentype/spec/recom + * under "Non-Standard (Symbol) Fonts". */ + return 0xF000u + codepoint; + } + return 0; +} + struct cmap { - static const hb_tag_t tableTag = HB_OT_TAG_cmap; + static constexpr hb_tag_t tableTag = HB_OT_TAG_cmap; - inline bool sanitize (hb_sanitize_context_t *c) const + + static SubtableUnicodesCache* create_filled_cache(hb_blob_ptr_t source_table) { + const cmap* cmap = source_table.get(); + auto it = + + hb_iter (cmap->encodingRecord) + | hb_filter ([&](const EncodingRecord& _) { + return cmap::filter_encoding_records_for_subset (cmap, _); + }) + ; + + SubtableUnicodesCache* cache = SubtableUnicodesCache::create(source_table); + for (const EncodingRecord& _ : it) + cache->set_for(&_); // populate the cache for this encoding record. + + return cache; + } + + template + bool serialize (hb_serialize_context_t *c, + Iterator it, + EncodingRecIter encodingrec_iter, + const void *base, + hb_subset_plan_t *plan, + bool drop_format_4 = false) + { + if (unlikely (!c->extend_min ((*this)))) return false; + this->version = 0; + + unsigned format4objidx = 0, format12objidx = 0, format14objidx = 0; + auto snap = c->snapshot (); + + SubtableUnicodesCache local_unicodes_cache (base); + const SubtableUnicodesCache* unicodes_cache = &local_unicodes_cache; + + if (plan->accelerator && + plan->accelerator->cmap_cache && + plan->accelerator->cmap_cache->same_base (base)) + unicodes_cache = plan->accelerator->cmap_cache; + + for (const EncodingRecord& _ : encodingrec_iter) + { + if (c->in_error ()) + return false; + + unsigned format = (base+_.subtable).u.format; + if (format != 4 && format != 12 && format != 14) continue; + + const hb_set_t* unicodes_set = unicodes_cache->set_for (&_, local_unicodes_cache); + + if (!drop_format_4 && format == 4) + { + c->copy (_, + it | hb_filter (*unicodes_set, hb_first), 4u, base, plan, &format4objidx); + if (c->in_error () && c->only_overflow ()) + { + // cmap4 overflowed, reset and retry serialization without format 4 subtables. + c->revert (snap); + return serialize (c, it, + encodingrec_iter, + base, + plan, + true); + } + } + + else if (format == 12) + { + if (_can_drop (_, + *unicodes_set, + base, + *unicodes_cache, + local_unicodes_cache, + + it | hb_map (hb_first), encodingrec_iter)) + continue; + c->copy (_, + it | hb_filter (*unicodes_set, hb_first), 12u, base, plan, &format12objidx); + } + else if (format == 14) c->copy (_, it, 14u, base, plan, &format14objidx); + } + c->check_assign(this->encodingRecord.len, + (c->length () - cmap::min_size)/EncodingRecord::static_size, + HB_SERIALIZE_ERROR_INT_OVERFLOW); + + // Fail if format 4 was dropped and there is no cmap12. + return !drop_format_4 || format12objidx; + } + + template + bool _can_drop (const EncodingRecord& cmap12, + const hb_set_t& cmap12_unicodes, + const void* base, + const SubtableUnicodesCache& unicodes_cache, + SubtableUnicodesCache& local_unicodes_cache, + Iterator subset_unicodes, + EncodingRecordIterator encoding_records) + { + for (auto cp : + subset_unicodes | hb_filter (cmap12_unicodes)) + { + if (cp >= 0x10000) return false; + } + + unsigned target_platform; + unsigned target_encoding; + unsigned target_language = (base+cmap12.subtable).get_language (); + + if (cmap12.platformID == 0 && cmap12.encodingID == 4) + { + target_platform = 0; + target_encoding = 3; + } else if (cmap12.platformID == 3 && cmap12.encodingID == 10) { + target_platform = 3; + target_encoding = 1; + } else { + return false; + } + + for (const auto& _ : encoding_records) + { + if (_.platformID != target_platform + || _.encodingID != target_encoding + || (base+_.subtable).get_language() != target_language) + continue; + + const hb_set_t* sibling_unicodes = unicodes_cache.set_for (&_, local_unicodes_cache); + + auto cmap12 = + subset_unicodes | hb_filter (cmap12_unicodes); + auto sibling = + subset_unicodes | hb_filter (*sibling_unicodes); + for (; cmap12 && sibling; cmap12++, sibling++) + { + unsigned a = *cmap12; + unsigned b = *sibling; + if (a != b) return false; + } + + return !cmap12 && !sibling; + } + + return false; + } + + void closure_glyphs (const hb_set_t *unicodes, + hb_set_t *glyphset) const + { + + hb_iter (encodingRecord) + | hb_map (&EncodingRecord::subtable) + | hb_map (hb_add (this)) + | hb_filter ([&] (const CmapSubtable& _) { return _.u.format == 14; }) + | hb_apply ([=] (const CmapSubtable& _) { _.u.format14.closure_glyphs (unicodes, glyphset); }) + ; + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + cmap *cmap_prime = c->serializer->start_embed (); + + auto encodingrec_iter = + + hb_iter (encodingRecord) + | hb_filter ([&](const EncodingRecord& _) { + return cmap::filter_encoding_records_for_subset (this, _); + }) + ; + + if (unlikely (!encodingrec_iter.len ())) return_trace (false); + + const EncodingRecord *unicode_bmp= nullptr, *unicode_ucs4 = nullptr, *ms_bmp = nullptr, *ms_ucs4 = nullptr; + bool has_format12 = false; + + for (const EncodingRecord& _ : encodingrec_iter) + { + unsigned format = (this + _.subtable).u.format; + if (format == 12) has_format12 = true; + + const EncodingRecord *table = std::addressof (_); + if (_.platformID == 0 && _.encodingID == 3) unicode_bmp = table; + else if (_.platformID == 0 && _.encodingID == 4) unicode_ucs4 = table; + else if (_.platformID == 3 && _.encodingID == 1) ms_bmp = table; + else if (_.platformID == 3 && _.encodingID == 10) ms_ucs4 = table; + } + + if (unlikely (!has_format12 && !unicode_bmp && !ms_bmp)) return_trace (false); + if (unlikely (has_format12 && (!unicode_ucs4 && !ms_ucs4))) return_trace (false); + + auto it = + + c->plan->unicode_to_new_gid_list.iter () + | hb_filter ([&] (const hb_codepoint_pair_t _) + { return (_.second != HB_MAP_VALUE_INVALID); }) + ; + + return_trace (cmap_prime->serialize (c->serializer, + it, + encodingrec_iter, + this, + c->plan)); + } + + const CmapSubtable *find_best_subtable (bool *symbol = nullptr) const + { + if (symbol) *symbol = false; + + const CmapSubtable *subtable; + + /* Symbol subtable. + * Prefer symbol if available. + * https://github.com/harfbuzz/harfbuzz/issues/1918 */ + if ((subtable = this->find_subtable (3, 0))) + { + if (symbol) *symbol = true; + return subtable; + } + + /* 32-bit subtables. */ + if ((subtable = this->find_subtable (3, 10))) return subtable; + if ((subtable = this->find_subtable (0, 6))) return subtable; + if ((subtable = this->find_subtable (0, 4))) return subtable; + + /* 16-bit subtables. */ + if ((subtable = this->find_subtable (3, 1))) return subtable; + if ((subtable = this->find_subtable (0, 3))) return subtable; + if ((subtable = this->find_subtable (0, 2))) return subtable; + if ((subtable = this->find_subtable (0, 1))) return subtable; + if ((subtable = this->find_subtable (0, 0))) return subtable; + + /* Meh. */ + return &Null (CmapSubtable); + } + + struct accelerator_t + { + using cache_t = hb_cache_t<21, 16, 8, true>; + + accelerator_t (hb_face_t *face) + { + this->table = hb_sanitize_context_t ().reference_table (face); + bool symbol; + this->subtable = table->find_best_subtable (&symbol); + this->subtable_uvs = &Null (CmapSubtableFormat14); + { + const CmapSubtable *st = table->find_subtable (0, 5); + if (st && st->u.format == 14) + subtable_uvs = &st->u.format14; + } + + this->get_glyph_data = subtable; + if (unlikely (symbol)) + { + switch ((unsigned) face->table.OS2->get_font_page ()) { + case OS2::font_page_t::FONT_PAGE_NONE: + this->get_glyph_funcZ = get_glyph_from_symbol; + break; +#ifndef HB_NO_OT_SHAPER_ARABIC_FALLBACK + case OS2::font_page_t::FONT_PAGE_SIMP_ARABIC: + this->get_glyph_funcZ = get_glyph_from_symbol; + break; + case OS2::font_page_t::FONT_PAGE_TRAD_ARABIC: + this->get_glyph_funcZ = get_glyph_from_symbol; + break; +#endif + default: + this->get_glyph_funcZ = get_glyph_from; + break; + } + } + else + { + switch (subtable->u.format) { + /* Accelerate format 4 and format 12. */ + default: + this->get_glyph_funcZ = get_glyph_from; + break; + case 12: + this->get_glyph_funcZ = get_glyph_from; + break; + case 4: + { + this->format4_accel.init (&subtable->u.format4); + this->get_glyph_data = &this->format4_accel; + this->get_glyph_funcZ = this->format4_accel.get_glyph_func; + break; + } + } + } + } + ~accelerator_t () { this->table.destroy (); } + + inline bool _cached_get (hb_codepoint_t unicode, + hb_codepoint_t *glyph, + cache_t *cache) const + { + unsigned v; + if (cache && cache->get (unicode, &v)) + { + *glyph = v; + return true; + } + bool ret = this->get_glyph_funcZ (this->get_glyph_data, unicode, glyph); + + if (cache && ret) + cache->set (unicode, *glyph); + return ret; + } + + bool get_nominal_glyph (hb_codepoint_t unicode, + hb_codepoint_t *glyph, + cache_t *cache = nullptr) const + { + if (unlikely (!this->get_glyph_funcZ)) return 0; + return _cached_get (unicode, glyph, cache); + } + + unsigned int get_nominal_glyphs (unsigned int count, + const hb_codepoint_t *first_unicode, + unsigned int unicode_stride, + hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + cache_t *cache = nullptr) const + { + if (unlikely (!this->get_glyph_funcZ)) return 0; + + unsigned int done; + for (done = 0; + done < count && _cached_get (*first_unicode, first_glyph, cache); + done++) + { + first_unicode = &StructAtOffsetUnaligned (first_unicode, unicode_stride); + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + } + return done; + } + + bool get_variation_glyph (hb_codepoint_t unicode, + hb_codepoint_t variation_selector, + hb_codepoint_t *glyph, + cache_t *cache = nullptr) const + { + switch (this->subtable_uvs->get_glyph_variant (unicode, + variation_selector, + glyph)) + { + case GLYPH_VARIANT_NOT_FOUND: return false; + case GLYPH_VARIANT_FOUND: return true; + case GLYPH_VARIANT_USE_DEFAULT: break; + } + + return get_nominal_glyph (unicode, glyph, cache); + } + + void collect_unicodes (hb_set_t *out, unsigned int num_glyphs) const + { subtable->collect_unicodes (out, num_glyphs); } + void collect_mapping (hb_set_t *unicodes, hb_map_t *mapping, + unsigned num_glyphs = UINT_MAX) const + { subtable->collect_mapping (unicodes, mapping, num_glyphs); } + void collect_variation_selectors (hb_set_t *out) const + { subtable_uvs->collect_variation_selectors (out); } + void collect_variation_unicodes (hb_codepoint_t variation_selector, + hb_set_t *out) const + { subtable_uvs->collect_variation_unicodes (variation_selector, out); } + + protected: + typedef bool (*hb_cmap_get_glyph_func_t) (const void *obj, + hb_codepoint_t codepoint, + hb_codepoint_t *glyph); + typedef uint_fast16_t (*hb_pua_remap_func_t) (unsigned); + + template + HB_INTERNAL static bool get_glyph_from (const void *obj, + hb_codepoint_t codepoint, + hb_codepoint_t *glyph) + { + const Type *typed_obj = (const Type *) obj; + return typed_obj->get_glyph (codepoint, glyph); + } + + template + HB_INTERNAL static bool get_glyph_from_symbol (const void *obj, + hb_codepoint_t codepoint, + hb_codepoint_t *glyph) + { + const Type *typed_obj = (const Type *) obj; + if (likely (typed_obj->get_glyph (codepoint, glyph))) + return true; + + if (hb_codepoint_t c = remap (codepoint)) + return typed_obj->get_glyph (c, glyph); + + return false; + } + + private: + hb_nonnull_ptr_t subtable; + hb_nonnull_ptr_t subtable_uvs; + + hb_cmap_get_glyph_func_t get_glyph_funcZ; + const void *get_glyph_data; + + CmapSubtableFormat4::accelerator_t format4_accel; + + public: + hb_blob_ptr_t table; + }; + + protected: + + const CmapSubtable *find_subtable (unsigned int platform_id, + unsigned int encoding_id) const + { + EncodingRecord key; + key.platformID = platform_id; + key.encodingID = encoding_id; + + const EncodingRecord &result = encodingRecord.bsearch (key); + if (!result.subtable) + return nullptr; + + return &(this+result.subtable); + } + + const EncodingRecord *find_encodingrec (unsigned int platform_id, + unsigned int encoding_id) const + { + EncodingRecord key; + key.platformID = platform_id; + key.encodingID = encoding_id; + + return encodingRecord.as_array ().bsearch (key); + } + + bool find_subtable (unsigned format) const + { + auto it = + + hb_iter (encodingRecord) + | hb_map (&EncodingRecord::subtable) + | hb_map (hb_add (this)) + | hb_filter ([&] (const CmapSubtable& _) { return _.u.format == format; }) + ; + + return it.len (); + } + + public: + + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && @@ -504,163 +2064,30 @@ struct cmap encodingRecord.sanitize (c, this)); } - struct accelerator_t + private: + + static bool filter_encoding_records_for_subset(const cmap* cmap, + const EncodingRecord& _) { - inline void init (hb_face_t *face) - { - this->blob = OT::Sanitizer::sanitize (face->reference_table (HB_OT_TAG_cmap)); - const OT::cmap *cmap = OT::Sanitizer::lock_instance (this->blob); - const OT::CmapSubtable *subtable = nullptr; - const OT::CmapSubtableFormat14 *subtable_uvs = nullptr; - - bool symbol = false; - /* 32-bit subtables. */ - if (!subtable) subtable = cmap->find_subtable (3, 10); - if (!subtable) subtable = cmap->find_subtable (0, 6); - if (!subtable) subtable = cmap->find_subtable (0, 4); - /* 16-bit subtables. */ - if (!subtable) subtable = cmap->find_subtable (3, 1); - if (!subtable) subtable = cmap->find_subtable (0, 3); - if (!subtable) subtable = cmap->find_subtable (0, 2); - if (!subtable) subtable = cmap->find_subtable (0, 1); - if (!subtable) subtable = cmap->find_subtable (0, 0); - if (!subtable) - { - subtable = cmap->find_subtable (3, 0); - if (subtable) symbol = true; - } - /* Meh. */ - if (!subtable) subtable = &OT::Null(OT::CmapSubtable); - - /* UVS subtable. */ - if (!subtable_uvs) - { - const OT::CmapSubtable *st = cmap->find_subtable (0, 5); - if (st && st->u.format == 14) - subtable_uvs = &st->u.format14; - } - /* Meh. */ - if (!subtable_uvs) subtable_uvs = &OT::Null(OT::CmapSubtableFormat14); - - this->uvs_table = subtable_uvs; - - this->get_glyph_data = subtable; - if (unlikely (symbol)) - this->get_glyph_func = get_glyph_from_symbol; - else - switch (subtable->u.format) { - /* Accelerate format 4 and format 12. */ - default: this->get_glyph_func = get_glyph_from; break; - case 12: this->get_glyph_func = get_glyph_from; break; - case 4: - { - this->format4_accel.init (&subtable->u.format4); - this->get_glyph_data = &this->format4_accel; - this->get_glyph_func = this->format4_accel.get_glyph_func; - } - break; - } - } - - inline void fini (void) - { - hb_blob_destroy (this->blob); - } - - inline bool get_nominal_glyph (hb_codepoint_t unicode, - hb_codepoint_t *glyph) const - { - return this->get_glyph_func (this->get_glyph_data, unicode, glyph); - } - - inline bool get_variation_glyph (hb_codepoint_t unicode, - hb_codepoint_t variation_selector, - hb_codepoint_t *glyph) const - { - switch (this->uvs_table->get_glyph_variant (unicode, - variation_selector, - glyph)) - { - case OT::GLYPH_VARIANT_NOT_FOUND: return false; - case OT::GLYPH_VARIANT_FOUND: return true; - case OT::GLYPH_VARIANT_USE_DEFAULT: break; - } - - return get_nominal_glyph (unicode, glyph); - } - - protected: - typedef bool (*hb_cmap_get_glyph_func_t) (const void *obj, - hb_codepoint_t codepoint, - hb_codepoint_t *glyph); - - template - static inline bool get_glyph_from (const void *obj, - hb_codepoint_t codepoint, - hb_codepoint_t *glyph) - { - const Type *typed_obj = (const Type *) obj; - return typed_obj->get_glyph (codepoint, glyph); - } - - template - static inline bool get_glyph_from_symbol (const void *obj, - hb_codepoint_t codepoint, - hb_codepoint_t *glyph) - { - const Type *typed_obj = (const Type *) obj; - if (likely (typed_obj->get_glyph (codepoint, glyph))) - return true; - - if (codepoint <= 0x00FFu) - { - /* For symbol-encoded OpenType fonts, we duplicate the - * U+F000..F0FF range at U+0000..U+00FF. That's what - * Windows seems to do, and that's hinted about at: - * http://www.microsoft.com/typography/otspec/recom.htm - * under "Non-Standard (Symbol) Fonts". */ - return typed_obj->get_glyph (0xF000u + codepoint, glyph); - } - - return false; - } - - private: - hb_cmap_get_glyph_func_t get_glyph_func; - const void *get_glyph_data; - OT::CmapSubtableFormat4::accelerator_t format4_accel; - - const OT::CmapSubtableFormat14 *uvs_table; - hb_blob_t *blob; - }; - - protected: - - inline const CmapSubtable *find_subtable (unsigned int platform_id, - unsigned int encoding_id) const - { - EncodingRecord key; - key.platformID.set (platform_id); - key.encodingID.set (encoding_id); - - /* Note: We can use bsearch, but since it has no performance - * implications, we use lsearch and as such accept fonts with - * unsorted subtable list. */ - int result = encodingRecord./*bsearch*/lsearch (key); - if (result == -1 || !encodingRecord[result].subtable) - return nullptr; - - return &(this+encodingRecord[result].subtable); + return + (_.platformID == 0 && _.encodingID == 3) || + (_.platformID == 0 && _.encodingID == 4) || + (_.platformID == 3 && _.encodingID == 1) || + (_.platformID == 3 && _.encodingID == 10) || + (cmap + _.subtable).u.format == 14; } protected: - UINT16 version; /* Table version number (0). */ - SortedArrayOf - encodingRecord; /* Encoding tables. */ + HBUINT16 version; /* Table version number (0). */ + SortedArray16Of + encodingRecord; /* Encoding tables. */ public: DEFINE_SIZE_ARRAY (4, encodingRecord); }; +struct cmap_accelerator_t : cmap::accelerator_t { + cmap_accelerator_t (hb_face_t *face) : cmap::accelerator_t (face) {} +}; } /* namespace OT */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-color.cc b/src/3rdparty/harfbuzz-ng/src/hb-ot-color.cc new file mode 100644 index 00000000000..37d42e08d94 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-color.cc @@ -0,0 +1,363 @@ +/* + * Copyright © 2016 Google, Inc. + * Copyright © 2018 Ebrahim Byagowi + * + * 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): Sascha Brawer, Behdad Esfahbod + */ + +#include "hb.hh" + +#ifndef HB_NO_COLOR + +#include "hb-ot.h" + +#include "OT/Color/CBDT/CBDT.hh" +#include "OT/Color/COLR/COLR.hh" +#include "OT/Color/CPAL/CPAL.hh" +#include "OT/Color/sbix/sbix.hh" +#include "OT/Color/svg/svg.hh" + + +/** + * SECTION:hb-ot-color + * @title: hb-ot-color + * @short_description: OpenType Color Fonts + * @include: hb-ot.h + * + * Functions for fetching color-font information from OpenType font faces. + * + * HarfBuzz supports `COLR`/`CPAL`, `sbix`, `CBDT`, and `SVG` color fonts. + **/ + + +/* + * CPAL + */ + + +/** + * hb_ot_color_has_palettes: + * @face: #hb_face_t to work upon + * + * Tests whether a face includes a `CPAL` color-palette table. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 2.1.0 + */ +hb_bool_t +hb_ot_color_has_palettes (hb_face_t *face) +{ + return face->table.CPAL->has_data (); +} + +/** + * hb_ot_color_palette_get_count: + * @face: #hb_face_t to work upon + * + * Fetches the number of color palettes in a face. + * + * Return value: the number of palettes found + * + * Since: 2.1.0 + */ +unsigned int +hb_ot_color_palette_get_count (hb_face_t *face) +{ + return face->table.CPAL->get_palette_count (); +} + +/** + * hb_ot_color_palette_get_name_id: + * @face: #hb_face_t to work upon + * @palette_index: The index of the color palette + * + * Fetches the `name` table Name ID that provides display names for + * a `CPAL` color palette. + * + * Palette display names can be generic (e.g., "Default") or provide + * specific, themed names (e.g., "Spring", "Summer", "Fall", and "Winter"). + * + * Return value: the Named ID found for the palette. + * If the requested palette has no name the result is #HB_OT_NAME_ID_INVALID. + * + * Since: 2.1.0 + */ +hb_ot_name_id_t +hb_ot_color_palette_get_name_id (hb_face_t *face, + unsigned int palette_index) +{ + return face->table.CPAL->get_palette_name_id (palette_index); +} + +/** + * hb_ot_color_palette_color_get_name_id: + * @face: #hb_face_t to work upon + * @color_index: The index of the color + * + * Fetches the `name` table Name ID that provides display names for + * the specified color in a face's `CPAL` color palette. + * + * Display names can be generic (e.g., "Background") or specific + * (e.g., "Eye color"). + * + * Return value: the Name ID found for the color. + * + * Since: 2.1.0 + */ +hb_ot_name_id_t +hb_ot_color_palette_color_get_name_id (hb_face_t *face, + unsigned int color_index) +{ + return face->table.CPAL->get_color_name_id (color_index); +} + +/** + * hb_ot_color_palette_get_flags: + * @face: #hb_face_t to work upon + * @palette_index: The index of the color palette + * + * Fetches the flags defined for a color palette. + * + * Return value: the #hb_ot_color_palette_flags_t of the requested color palette + * + * Since: 2.1.0 + */ +hb_ot_color_palette_flags_t +hb_ot_color_palette_get_flags (hb_face_t *face, + unsigned int palette_index) +{ + return face->table.CPAL->get_palette_flags (palette_index); +} + +/** + * hb_ot_color_palette_get_colors: + * @face: #hb_face_t to work upon + * @palette_index: the index of the color palette to query + * @start_offset: offset of the first color to retrieve + * @color_count: (inout) (optional): Input = the maximum number of colors to return; + * Output = the actual number of colors returned (may be zero) + * @colors: (out) (array length=color_count) (nullable): The array of #hb_color_t records found + * + * Fetches a list of the colors in a color palette. + * + * After calling this function, @colors will be filled with the palette + * colors. If @colors is NULL, the function will just return the number + * of total colors without storing any actual colors; this can be used + * for allocating a buffer of suitable size before calling + * hb_ot_color_palette_get_colors() a second time. + * + * The RGBA values in the palette are unpremultiplied. See the + * OpenType spec [CPAL](https://learn.microsoft.com/en-us/typography/opentype/spec/cpal) + * section for details. + * + * Return value: the total number of colors in the palette + * + * Since: 2.1.0 + */ +unsigned int +hb_ot_color_palette_get_colors (hb_face_t *face, + unsigned int palette_index, + unsigned int start_offset, + unsigned int *colors_count /* IN/OUT. May be NULL. */, + hb_color_t *colors /* OUT. May be NULL. */) +{ + return face->table.CPAL->get_palette_colors (palette_index, start_offset, colors_count, colors); +} + + +/* + * COLR + */ + +/** + * hb_ot_color_has_layers: + * @face: #hb_face_t to work upon + * + * Tests whether a face includes a `COLR` table + * with data according to COLRv0. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 2.1.0 + */ +hb_bool_t +hb_ot_color_has_layers (hb_face_t *face) +{ + return face->table.COLR->has_v0_data (); +} + +/** + * hb_ot_color_has_paint: + * @face: #hb_face_t to work upon + * + * Tests where a face includes a `COLR` table + * with data according to COLRv1. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 7.0.0 + */ +hb_bool_t +hb_ot_color_has_paint (hb_face_t *face) +{ + return face->table.COLR->has_v1_data (); +} + +/** + * hb_ot_color_glyph_has_paint: + * @face: #hb_face_t to work upon + * @glyph: The glyph index to query + * + * Tests where a face includes COLRv1 paint + * data for @glyph. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 7.0.0 + */ +hb_bool_t +hb_ot_color_glyph_has_paint (hb_face_t *face, + hb_codepoint_t glyph) +{ + return face->table.COLR->has_paint_for_glyph (glyph); +} + +/** + * hb_ot_color_glyph_get_layers: + * @face: #hb_face_t to work upon + * @glyph: The glyph index to query + * @start_offset: offset of the first layer to retrieve + * @layer_count: (inout) (optional): Input = the maximum number of layers to return; + * Output = the actual number of layers returned (may be zero) + * @layers: (out) (array length=layer_count) (nullable): The array of layers found + * + * Fetches a list of all color layers for the specified glyph index in the specified + * face. The list returned will begin at the offset provided. + * + * Return value: Total number of layers available for the glyph index queried + * + * Since: 2.1.0 + */ +unsigned int +hb_ot_color_glyph_get_layers (hb_face_t *face, + hb_codepoint_t glyph, + unsigned int start_offset, + unsigned int *layer_count, /* IN/OUT. May be NULL. */ + hb_ot_color_layer_t *layers /* OUT. May be NULL. */) +{ + return face->table.COLR->get_glyph_layers (glyph, start_offset, layer_count, layers); +} + + +/* + * SVG + */ + +/** + * hb_ot_color_has_svg: + * @face: #hb_face_t to work upon. + * + * Tests whether a face includes any `SVG` glyph images. + * + * Return value: `true` if data found, `false` otherwise. + * + * Since: 2.1.0 + */ +hb_bool_t +hb_ot_color_has_svg (hb_face_t *face) +{ + return face->table.SVG->has_data (); +} + +/** + * hb_ot_color_glyph_reference_svg: + * @face: #hb_face_t to work upon + * @glyph: a svg glyph index + * + * Fetches the SVG document for a glyph. The blob may be either plain text or gzip-encoded. + * + * If the glyph has no SVG document, the singleton empty blob is returned. + * + * Return value: (transfer full): An #hb_blob_t containing the SVG document of the glyph, if available + * + * Since: 2.1.0 + */ +hb_blob_t * +hb_ot_color_glyph_reference_svg (hb_face_t *face, hb_codepoint_t glyph) +{ + return face->table.SVG->reference_blob_for_glyph (glyph); +} + + +/* + * PNG: CBDT or sbix + */ + +/** + * hb_ot_color_has_png: + * @face: #hb_face_t to work upon + * + * Tests whether a face has PNG glyph images (either in `CBDT` or `sbix` tables). + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 2.1.0 + */ +hb_bool_t +hb_ot_color_has_png (hb_face_t *face) +{ + return face->table.CBDT->has_data () || face->table.sbix->has_data (); +} + +/** + * hb_ot_color_glyph_reference_png: + * @font: #hb_font_t to work upon + * @glyph: a glyph index + * + * Fetches the PNG image for a glyph. This function takes a font object, not a face object, + * as input. To get an optimally sized PNG blob, the PPEM values must be set on the @font + * object. If PPEM is unset, the blob returned will be the largest PNG available. + * + * If the glyph has no PNG image, the singleton empty blob is returned. + * + * Return value: (transfer full): An #hb_blob_t containing the PNG image for the glyph, if available + * + * Since: 2.1.0 + */ +hb_blob_t * +hb_ot_color_glyph_reference_png (hb_font_t *font, hb_codepoint_t glyph) +{ + hb_blob_t *blob = hb_blob_get_empty (); + + if (font->face->table.sbix->has_data ()) + blob = font->face->table.sbix->reference_png (font, glyph, nullptr, nullptr, nullptr); + + if (!blob->length && font->face->table.CBDT->has_data ()) + blob = font->face->table.CBDT->reference_png (font, glyph); + + return blob; +} + + +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-color.h b/src/3rdparty/harfbuzz-ng/src/hb-ot-color.h new file mode 100644 index 00000000000..22ee497e388 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-color.h @@ -0,0 +1,155 @@ +/* + * Copyright © 2016 Google, Inc. + * Copyright © 2018 Khaled Hosny + * Copyright © 2018 Ebrahim Byagowi + * + * 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): Sascha Brawer, Behdad Esfahbod + */ + +#if !defined(HB_OT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_OT_COLOR_H +#define HB_OT_COLOR_H + +#include "hb.h" +#include "hb-ot-name.h" + +HB_BEGIN_DECLS + + +/* + * Color palettes. + */ + +HB_EXTERN hb_bool_t +hb_ot_color_has_palettes (hb_face_t *face); + +HB_EXTERN unsigned int +hb_ot_color_palette_get_count (hb_face_t *face); + +HB_EXTERN hb_ot_name_id_t +hb_ot_color_palette_get_name_id (hb_face_t *face, + unsigned int palette_index); + +HB_EXTERN hb_ot_name_id_t +hb_ot_color_palette_color_get_name_id (hb_face_t *face, + unsigned int color_index); + +/** + * hb_ot_color_palette_flags_t: + * @HB_OT_COLOR_PALETTE_FLAG_DEFAULT: Default indicating that there is nothing special + * to note about a color palette. + * @HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_LIGHT_BACKGROUND: Flag indicating that the color + * palette is appropriate to use when displaying the font on a light background such as white. + * @HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_DARK_BACKGROUND: Flag indicating that the color + * palette is appropriate to use when displaying the font on a dark background such as black. + * + * Flags that describe the properties of color palette. + * + * Since: 2.1.0 + */ +typedef enum { /*< flags >*/ + HB_OT_COLOR_PALETTE_FLAG_DEFAULT = 0x00000000u, + HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_LIGHT_BACKGROUND = 0x00000001u, + HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_DARK_BACKGROUND = 0x00000002u +} hb_ot_color_palette_flags_t; + +HB_EXTERN hb_ot_color_palette_flags_t +hb_ot_color_palette_get_flags (hb_face_t *face, + unsigned int palette_index); + +HB_EXTERN unsigned int +hb_ot_color_palette_get_colors (hb_face_t *face, + unsigned int palette_index, + unsigned int start_offset, + unsigned int *color_count, /* IN/OUT. May be NULL. */ + hb_color_t *colors /* OUT. May be NULL. */); + + +/* + * Color layers. + */ + +HB_EXTERN hb_bool_t +hb_ot_color_has_layers (hb_face_t *face); + +/** + * hb_ot_color_layer_t: + * @glyph: the glyph ID of the layer + * @color_index: the palette color index of the layer + * + * Pairs of glyph and color index. + * + * A color index of 0xFFFF does not refer to a palette + * color, but indicates that the foreground color should + * be used. + * + * Since: 2.1.0 + **/ +typedef struct hb_ot_color_layer_t { + hb_codepoint_t glyph; + unsigned int color_index; +} hb_ot_color_layer_t; + +HB_EXTERN unsigned int +hb_ot_color_glyph_get_layers (hb_face_t *face, + hb_codepoint_t glyph, + unsigned int start_offset, + unsigned int *layer_count, /* IN/OUT. May be NULL. */ + hb_ot_color_layer_t *layers /* OUT. May be NULL. */); + +/* COLRv1 */ + +HB_EXTERN hb_bool_t +hb_ot_color_has_paint (hb_face_t *face); + +HB_EXTERN hb_bool_t +hb_ot_color_glyph_has_paint (hb_face_t *face, + hb_codepoint_t glyph); + +/* + * SVG + */ + +HB_EXTERN hb_bool_t +hb_ot_color_has_svg (hb_face_t *face); + +HB_EXTERN hb_blob_t * +hb_ot_color_glyph_reference_svg (hb_face_t *face, hb_codepoint_t glyph); + +/* + * PNG: CBDT or sbix + */ + +HB_EXTERN hb_bool_t +hb_ot_color_has_png (hb_face_t *face); + +HB_EXTERN hb_blob_t * +hb_ot_color_glyph_reference_png (hb_font_t *font, hb_codepoint_t glyph); + + +HB_END_DECLS + +#endif /* HB_OT_COLOR_H */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-deprecated.h b/src/3rdparty/harfbuzz-ng/src/hb-ot-deprecated.h new file mode 100644 index 00000000000..60672ab1283 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-deprecated.h @@ -0,0 +1,147 @@ +/* + * 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): Behdad Esfahbod + */ + +#if !defined(HB_OT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_OT_DEPRECATED_H +#define HB_OT_DEPRECATED_H + +#include "hb.h" +#include "hb-ot-name.h" + + +HB_BEGIN_DECLS + +#ifndef HB_DISABLE_DEPRECATED + + +/* https://github.com/harfbuzz/harfbuzz/issues/1734 */ +/** + * HB_MATH_GLYPH_PART_FLAG_EXTENDER: + * + * Use #HB_OT_MATH_GLYPH_PART_FLAG_EXTENDER instead. + * + * Deprecated: 2.5.1 + */ +#define HB_MATH_GLYPH_PART_FLAG_EXTENDER HB_OT_MATH_GLYPH_PART_FLAG_EXTENDER + +/* https://github.com/harfbuzz/harfbuzz/pull/3417 */ +/** + * HB_OT_MATH_SCRIPT: + * + * Use #HB_SCRIPT_MATH or #HB_OT_TAG_MATH_SCRIPT instead. + * + * Previous versions of this documentation recommended passing + * #HB_OT_MATH_SCRIPT to hb_buffer_set_script() to enable math shaping, but this + * usage is no longer supported. Use #HB_SCRIPT_MATH instead. + * + * Since: 1.3.3 + * Deprecated: 3.4.0 + */ +#define HB_OT_MATH_SCRIPT HB_OT_TAG_MATH_SCRIPT + + +/* Like hb_ot_layout_table_find_script, but takes zero-terminated array of scripts to test */ +HB_DEPRECATED_FOR (hb_ot_layout_table_select_script) +HB_EXTERN hb_bool_t +hb_ot_layout_table_choose_script (hb_face_t *face, + hb_tag_t table_tag, + const hb_tag_t *script_tags, + unsigned int *script_index, + hb_tag_t *chosen_script); + +HB_DEPRECATED_FOR (hb_ot_layout_script_select_language) +HB_EXTERN hb_bool_t +hb_ot_layout_script_find_language (hb_face_t *face, + hb_tag_t table_tag, + unsigned int script_index, + hb_tag_t language_tag, + unsigned int *language_index); + +HB_DEPRECATED_FOR (hb_ot_tags_from_script_and_language) +HB_EXTERN void +hb_ot_tags_from_script (hb_script_t script, + hb_tag_t *script_tag_1, + hb_tag_t *script_tag_2); + +HB_DEPRECATED_FOR (hb_ot_tags_from_script_and_language) +HB_EXTERN hb_tag_t +hb_ot_tag_from_language (hb_language_t language); + + +/** + * HB_OT_VAR_NO_AXIS_INDEX: + * + * Do not use. + * + * Since: 1.4.2 + * Deprecated: 2.2.0 + */ +#define HB_OT_VAR_NO_AXIS_INDEX 0xFFFFFFFFu + +/** + * hb_ot_var_axis_t: + * @tag: axis tag + * @name_id: axis name identifier + * @min_value: minimum value of the axis + * @default_value: default value of the axis + * @max_value: maximum value of the axis + * + * Use #hb_ot_var_axis_info_t instead. + * + * Since: 1.4.2 + * Deprecated: 2.2.0 + */ +typedef struct hb_ot_var_axis_t { + hb_tag_t tag; + hb_ot_name_id_t name_id; + float min_value; + float default_value; + float max_value; +} hb_ot_var_axis_t; + +HB_DEPRECATED_FOR (hb_ot_var_get_axis_infos) +HB_EXTERN unsigned int +hb_ot_var_get_axes (hb_face_t *face, + unsigned int start_offset, + unsigned int *axes_count /* IN/OUT */, + hb_ot_var_axis_t *axes_array /* OUT */); + +HB_DEPRECATED_FOR (hb_ot_var_find_axis_info) +HB_EXTERN hb_bool_t +hb_ot_var_find_axis (hb_face_t *face, + hb_tag_t axis_tag, + unsigned int *axis_index, + hb_ot_var_axis_t *axis_info); + + +#endif + +HB_END_DECLS + +#endif /* HB_OT_DEPRECATED_H */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-face-table-list.hh b/src/3rdparty/harfbuzz-ng/src/hb-ot-face-table-list.hh new file mode 100644 index 00000000000..b552dfdd9da --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-face-table-list.hh @@ -0,0 +1,152 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2012,2013 Google, Inc. + * Copyright © 2019, Facebook 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + * Facebook Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_FACE_TABLE_LIST_HH +#define HB_OT_FACE_TABLE_LIST_HH +#endif /* HB_OT_FACE_TABLE_LIST_HH */ /* Dummy header guards */ + +#ifndef HB_OT_CORE_TABLE +#define HB_OT_CORE_TABLE(Namespace, Type) HB_OT_TABLE (Namespace, Type) +#define _HB_OT_CORE_TABLE_UNDEF +#endif + +#ifndef HB_OT_ACCELERATOR +#define HB_OT_ACCELERATOR(Namespace, Type) HB_OT_TABLE (Namespace, Type) +#define _HB_OT_ACCELERATOR_UNDEF +#endif + + +/* This lists font tables that the hb_face_t will contain and lazily + * load. Don't add a table unless it's used though. This is not + * exactly zero-cost. */ + +/* v--- Add new tables in the right place here. */ + + +/* OpenType fundamentals. */ +HB_OT_CORE_TABLE (OT, head) +HB_OT_CORE_TABLE (OT, maxp) +#if !defined(HB_NO_FACE_COLLECT_UNICODES) || !defined(HB_NO_OT_FONT) +HB_OT_ACCELERATOR (OT, cmap) +#endif +HB_OT_CORE_TABLE (OT, hhea) +HB_OT_ACCELERATOR (OT, hmtx) +HB_OT_CORE_TABLE (OT, OS2) +#if !defined(HB_NO_OT_FONT_GLYPH_NAMES) || !defined(HB_NO_METRICS) || !defined(HB_NO_STYLE) +HB_OT_ACCELERATOR (OT, post) +#endif +#ifndef HB_NO_NAME +HB_OT_ACCELERATOR (OT, name) +#endif +#ifndef HB_NO_STYLE +HB_OT_CORE_TABLE (OT, STAT) +#endif +#ifndef HB_NO_META +HB_OT_ACCELERATOR (OT, meta) +#endif + +/* Vertical layout. */ +#ifndef HB_NO_VERTICAL +HB_OT_CORE_TABLE (OT, vhea) +HB_OT_ACCELERATOR (OT, vmtx) +HB_OT_CORE_TABLE (OT, VORG) +#endif + +/* TrueType outlines. */ +HB_OT_CORE_TABLE (OT, loca) // Also used to determine number of glyphs +HB_OT_ACCELERATOR (OT, glyf) + +/* CFF outlines. */ +#ifndef HB_NO_CFF +HB_OT_ACCELERATOR (OT, cff1) +HB_OT_ACCELERATOR (OT, cff2) +#endif + +/* OpenType variations. */ +#ifndef HB_NO_VAR +HB_OT_CORE_TABLE (OT, fvar) +HB_OT_CORE_TABLE (OT, avar) +HB_OT_CORE_TABLE (OT, cvar) +HB_OT_ACCELERATOR (OT, gvar) +HB_OT_CORE_TABLE (OT, MVAR) +#endif + +/* Legacy kern. */ +#ifndef HB_NO_OT_KERN +HB_OT_CORE_TABLE (OT, kern) +#endif + +/* OpenType shaping. */ +#ifndef HB_NO_OT_LAYOUT +HB_OT_ACCELERATOR (OT, GDEF) +HB_OT_ACCELERATOR (OT, GSUB) +HB_OT_ACCELERATOR (OT, GPOS) +//HB_OT_CORE_TABLE (OT, JSTF) +#endif + +/* OpenType baseline. */ +#ifndef HB_NO_BASE +HB_OT_CORE_TABLE (OT, BASE) +#endif + +/* AAT shaping. */ +#ifndef HB_NO_AAT +HB_OT_TABLE (AAT, morx) +HB_OT_TABLE (AAT, mort) +HB_OT_TABLE (AAT, kerx) +HB_OT_TABLE (AAT, ankr) +HB_OT_TABLE (AAT, trak) +HB_OT_TABLE (AAT, ltag) +HB_OT_TABLE (AAT, feat) +// HB_OT_TABLE (AAT, opbd) +#endif + +/* OpenType color fonts. */ +#ifndef HB_NO_COLOR +HB_OT_CORE_TABLE (OT, COLR) +HB_OT_CORE_TABLE (OT, CPAL) +HB_OT_ACCELERATOR (OT, CBDT) +HB_OT_ACCELERATOR (OT, sbix) +HB_OT_ACCELERATOR (OT, SVG) +#endif + +/* OpenType math. */ +#ifndef HB_NO_MATH +HB_OT_CORE_TABLE (OT, MATH) +#endif + + +#ifdef _HB_OT_ACCELERATOR_UNDEF +#undef HB_OT_ACCELERATOR +#endif + +#ifdef _HB_OT_CORE_TABLE_UNDEF +#undef HB_OT_CORE_TABLE +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-face.cc b/src/3rdparty/harfbuzz-ng/src/hb-ot-face.cc new file mode 100644 index 00000000000..2243ee02874 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-face.cc @@ -0,0 +1,58 @@ +/* + * 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): Behdad Esfahbod + */ + +#include "hb-ot-face.hh" + +#include "hb-ot-cmap-table.hh" +#include "hb-ot-glyf-table.hh" +#include "hb-ot-cff1-table.hh" +#include "hb-ot-cff2-table.hh" +#include "hb-ot-hmtx-table.hh" +#include "hb-ot-kern-table.hh" +#include "hb-ot-meta-table.hh" +#include "hb-ot-name-table.hh" +#include "hb-ot-post-table.hh" +#include "OT/Color/CBDT/CBDT.hh" +#include "OT/Color/sbix/sbix.hh" +#include "OT/Color/svg/svg.hh" +#include "hb-ot-layout-gdef-table.hh" +#include "hb-ot-layout-gsub-table.hh" +#include "hb-ot-layout-gpos-table.hh" + + +void hb_ot_face_t::init0 (hb_face_t *face) +{ + this->face = face; +#define HB_OT_TABLE(Namespace, Type) Type.init0 (); +#include "hb-ot-face-table-list.hh" +#undef HB_OT_TABLE +} +void hb_ot_face_t::fini () +{ +#define HB_OT_TABLE(Namespace, Type) Type.fini (); +#include "hb-ot-face-table-list.hh" +#undef HB_OT_TABLE +} diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-face.hh b/src/3rdparty/harfbuzz-ng/src/hb-ot-face.hh new file mode 100644 index 00000000000..415dae8e209 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-face.hh @@ -0,0 +1,77 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2012,2013 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_FACE_HH +#define HB_OT_FACE_HH + +#include "hb.hh" + +#include "hb-machinery.hh" + + +/* + * hb_ot_face_t + */ + +/* Declare tables. */ +#define HB_OT_TABLE(Namespace, Type) namespace Namespace { struct Type; } +#define HB_OT_ACCELERATOR(Namespace, Type) HB_OT_TABLE (Namespace, Type##_accelerator_t) +#include "hb-ot-face-table-list.hh" +#undef HB_OT_ACCELERATOR +#undef HB_OT_TABLE + +struct hb_ot_face_t +{ + HB_INTERNAL void init0 (hb_face_t *face); + HB_INTERNAL void fini (); + +#define HB_OT_TABLE_ORDER(Namespace, Type) \ + HB_PASTE (ORDER_, HB_PASTE (Namespace, HB_PASTE (_, Type))) + enum order_t + { + ORDER_ZERO, +#define HB_OT_TABLE(Namespace, Type) HB_OT_TABLE_ORDER (Namespace, Type), +#include "hb-ot-face-table-list.hh" +#undef HB_OT_TABLE + }; + + hb_face_t *face; /* MUST be JUST before the lazy loaders. */ +#define HB_OT_TABLE(Namespace, Type) \ + hb_table_lazy_loader_t Type; +#define HB_OT_CORE_TABLE(Namespace, Type) \ + hb_table_lazy_loader_t Type; +#define HB_OT_ACCELERATOR(Namespace, Type) \ + hb_face_lazy_loader_t Type; +#include "hb-ot-face-table-list.hh" +#undef HB_OT_ACCELERATOR +#undef HB_OT_CORE_TABLE +#undef HB_OT_TABLE +}; + + +#endif /* HB_OT_FACE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-font.cc b/src/3rdparty/harfbuzz-ng/src/hb-ot-font.cc index 9864064b112..b3677c6a4c0 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-ot-font.cc +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-font.cc @@ -24,78 +24,151 @@ * Google Author(s): Behdad Esfahbod, Roozbeh Pournader */ -#include "hb-private.hh" +#include "hb.hh" + +#ifndef HB_NO_OT_FONT #include "hb-ot.h" -#include "hb-font-private.hh" +#include "hb-cache.hh" +#include "hb-font.hh" +#include "hb-machinery.hh" +#include "hb-ot-face.hh" +#include "hb-outline.hh" #include "hb-ot-cmap-table.hh" -#include "hb-ot-cbdt-table.hh" #include "hb-ot-glyf-table.hh" +#include "hb-ot-cff2-table.hh" +#include "hb-ot-cff1-table.hh" #include "hb-ot-hmtx-table.hh" -#include "hb-ot-kern-table.hh" #include "hb-ot-post-table.hh" +#include "hb-ot-stat-table.hh" // Just so we compile it; unused otherwise. +#include "hb-ot-vorg-table.hh" +#include "OT/Color/CBDT/CBDT.hh" +#include "OT/Color/COLR/COLR.hh" +#include "OT/Color/sbix/sbix.hh" +#include "OT/Color/svg/svg.hh" +/** + * SECTION:hb-ot-font + * @title: hb-ot-font + * @short_description: OpenType font implementation + * @include: hb-ot.h + * + * Functions for using OpenType fonts with hb_shape(). Note that fonts returned + * by hb_font_create() default to using these functions, so most clients would + * never need to call these functions directly. + **/ + +using hb_ot_font_cmap_cache_t = hb_cache_t<21, 16, 8, true>; +using hb_ot_font_advance_cache_t = hb_cache_t<24, 16, 8, true>; + +#ifndef HB_NO_OT_FONT_CMAP_CACHE +static hb_user_data_key_t hb_ot_font_cmap_cache_user_data_key; +#endif + struct hb_ot_font_t { - OT::cmap::accelerator_t cmap; - OT::hmtx::accelerator_t h_metrics; - OT::vmtx::accelerator_t v_metrics; - OT::hb_lazy_loader_t glyf; - OT::hb_lazy_loader_t cbdt; - OT::hb_lazy_loader_t post; - OT::hb_lazy_loader_t kern; + const hb_ot_face_t *ot_face; + +#ifndef HB_NO_OT_FONT_CMAP_CACHE + hb_ot_font_cmap_cache_t *cmap_cache; +#endif + + /* h_advance caching */ + mutable hb_atomic_int_t cached_coords_serial; + mutable hb_atomic_ptr_t advance_cache; }; - static hb_ot_font_t * -_hb_ot_font_create (hb_face_t *face) +_hb_ot_font_create (hb_font_t *font) { - hb_ot_font_t *ot_font = (hb_ot_font_t *) calloc (1, sizeof (hb_ot_font_t)); - + hb_ot_font_t *ot_font = (hb_ot_font_t *) hb_calloc (1, sizeof (hb_ot_font_t)); if (unlikely (!ot_font)) return nullptr; - ot_font->cmap.init (face); - ot_font->h_metrics.init (face); - ot_font->v_metrics.init (face, ot_font->h_metrics.ascender - ot_font->h_metrics.descender); /* TODO Can we do this lazily? */ - ot_font->glyf.init (face); - ot_font->cbdt.init (face); - ot_font->post.init (face); - ot_font->kern.init (face); + ot_font->ot_face = &font->face->table; + +#ifndef HB_NO_OT_FONT_CMAP_CACHE + // retry: + auto *cmap_cache = (hb_ot_font_cmap_cache_t *) hb_face_get_user_data (font->face, + &hb_ot_font_cmap_cache_user_data_key); + if (!cmap_cache) + { + cmap_cache = (hb_ot_font_cmap_cache_t *) hb_malloc (sizeof (hb_ot_font_cmap_cache_t)); + if (unlikely (!cmap_cache)) goto out; + new (cmap_cache) hb_ot_font_cmap_cache_t (); + if (unlikely (!hb_face_set_user_data (font->face, + &hb_ot_font_cmap_cache_user_data_key, + cmap_cache, + hb_free, + false))) + { + hb_free (cmap_cache); + cmap_cache = nullptr; + /* Normally we would retry here, but that would + * infinite-loop if the face is the empty-face. + * Just let it go and this font will be uncached if it + * happened to collide with another thread creating the + * cache at the same time. */ + // goto retry; + } + } + out: + ot_font->cmap_cache = cmap_cache; +#endif return ot_font; } static void -_hb_ot_font_destroy (void *data) +_hb_ot_font_destroy (void *font_data) { - hb_ot_font_t *ot_font = (hb_ot_font_t *) data; + hb_ot_font_t *ot_font = (hb_ot_font_t *) font_data; - ot_font->cmap.fini (); - ot_font->h_metrics.fini (); - ot_font->v_metrics.fini (); - ot_font->glyf.fini (); - ot_font->cbdt.fini (); - ot_font->post.fini (); - ot_font->kern.fini (); + auto *cache = ot_font->advance_cache.get_relaxed (); + hb_free (cache); - free (ot_font); + hb_free (ot_font); } - static hb_bool_t hb_ot_get_nominal_glyph (hb_font_t *font HB_UNUSED, void *font_data, hb_codepoint_t unicode, hb_codepoint_t *glyph, void *user_data HB_UNUSED) - { const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; - return ot_font->cmap.get_nominal_glyph (unicode, glyph); + const hb_ot_face_t *ot_face = ot_font->ot_face; + hb_ot_font_cmap_cache_t *cmap_cache = nullptr; +#ifndef HB_NO_OT_FONT_CMAP_CACHE + cmap_cache = ot_font->cmap_cache; +#endif + return ot_face->cmap->get_nominal_glyph (unicode, glyph, cmap_cache); +} + +static unsigned int +hb_ot_get_nominal_glyphs (hb_font_t *font HB_UNUSED, + void *font_data, + unsigned int count, + const hb_codepoint_t *first_unicode, + unsigned int unicode_stride, + hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + void *user_data HB_UNUSED) +{ + const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; + const hb_ot_face_t *ot_face = ot_font->ot_face; + hb_ot_font_cmap_cache_t *cmap_cache = nullptr; +#ifndef HB_NO_OT_FONT_CMAP_CACHE + cmap_cache = ot_font->cmap_cache; +#endif + return ot_face->cmap->get_nominal_glyphs (count, + first_unicode, unicode_stride, + first_glyph, glyph_stride, + cmap_cache); } static hb_bool_t @@ -107,169 +180,468 @@ hb_ot_get_variation_glyph (hb_font_t *font HB_UNUSED, void *user_data HB_UNUSED) { const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; - return ot_font->cmap.get_variation_glyph (unicode, variation_selector, glyph); + const hb_ot_face_t *ot_face = ot_font->ot_face; + hb_ot_font_cmap_cache_t *cmap_cache = nullptr; +#ifndef HB_NO_OT_FONT_CMAP_CACHE + cmap_cache = ot_font->cmap_cache; +#endif + return ot_face->cmap->get_variation_glyph (unicode, + variation_selector, glyph, + cmap_cache); } -static hb_position_t -hb_ot_get_glyph_h_advance (hb_font_t *font, - void *font_data, - hb_codepoint_t glyph, - void *user_data HB_UNUSED) +static void +hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data, + unsigned count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride, + void *user_data HB_UNUSED) { + const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; - return font->em_scale_x (ot_font->h_metrics.get_advance (glyph, font)); + const hb_ot_face_t *ot_face = ot_font->ot_face; + const OT::hmtx_accelerator_t &hmtx = *ot_face->hmtx; + + hb_position_t *orig_first_advance = first_advance; + +#if !defined(HB_NO_VAR) && !defined(HB_NO_OT_FONT_ADVANCE_CACHE) + const OT::HVAR &HVAR = *hmtx.var_table; + const OT::VariationStore &varStore = &HVAR + HVAR.varStore; + OT::VariationStore::cache_t *varStore_cache = font->num_coords * count >= 128 ? varStore.create_cache () : nullptr; + + bool use_cache = font->num_coords; +#else + OT::VariationStore::cache_t *varStore_cache = nullptr; + bool use_cache = false; +#endif + + hb_ot_font_advance_cache_t *cache = nullptr; + if (use_cache) + { + retry: + cache = ot_font->advance_cache.get_acquire (); + if (unlikely (!cache)) + { + cache = (hb_ot_font_advance_cache_t *) hb_malloc (sizeof (hb_ot_font_advance_cache_t)); + if (unlikely (!cache)) + { + use_cache = false; + goto out; + } + new (cache) hb_ot_font_advance_cache_t; + + if (unlikely (!ot_font->advance_cache.cmpexch (nullptr, cache))) + { + hb_free (cache); + goto retry; + } + ot_font->cached_coords_serial.set_release (font->serial_coords); + } + } + out: + + if (!use_cache) + { + for (unsigned int i = 0; i < count; i++) + { + *first_advance = font->em_scale_x (hmtx.get_advance_with_var_unscaled (*first_glyph, font, varStore_cache)); + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + } + else + { /* Use cache. */ + if (ot_font->cached_coords_serial.get_acquire () != (int) font->serial_coords) + { + ot_font->advance_cache->clear (); + ot_font->cached_coords_serial.set_release (font->serial_coords); + } + + for (unsigned int i = 0; i < count; i++) + { + hb_position_t v; + unsigned cv; + if (ot_font->advance_cache->get (*first_glyph, &cv)) + v = cv; + else + { + v = hmtx.get_advance_with_var_unscaled (*first_glyph, font, varStore_cache); + ot_font->advance_cache->set (*first_glyph, v); + } + *first_advance = font->em_scale_x (v); + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + } + +#if !defined(HB_NO_VAR) && !defined(HB_NO_OT_FONT_ADVANCE_CACHE) + OT::VariationStore::destroy_cache (varStore_cache); +#endif + + if (font->x_strength && !font->embolden_in_place) + { + /* Emboldening. */ + hb_position_t x_strength = font->x_scale >= 0 ? font->x_strength : -font->x_strength; + first_advance = orig_first_advance; + for (unsigned int i = 0; i < count; i++) + { + *first_advance += *first_advance ? x_strength : 0; + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + } } -static hb_position_t -hb_ot_get_glyph_v_advance (hb_font_t *font, - void *font_data, - hb_codepoint_t glyph, - void *user_data HB_UNUSED) +#ifndef HB_NO_VERTICAL +static void +hb_ot_get_glyph_v_advances (hb_font_t* font, void* font_data, + unsigned count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride, + void *user_data HB_UNUSED) { const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; - return font->em_scale_y (-(int) ot_font->v_metrics.get_advance (glyph, font)); -} + const hb_ot_face_t *ot_face = ot_font->ot_face; + const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx; -static hb_position_t -hb_ot_get_glyph_h_kerning (hb_font_t *font, - void *font_data, - hb_codepoint_t left_glyph, - hb_codepoint_t right_glyph, - void *user_data HB_UNUSED) + hb_position_t *orig_first_advance = first_advance; + + if (vmtx.has_data ()) + { +#if !defined(HB_NO_VAR) && !defined(HB_NO_OT_FONT_ADVANCE_CACHE) + const OT::VVAR &VVAR = *vmtx.var_table; + const OT::VariationStore &varStore = &VVAR + VVAR.varStore; + OT::VariationStore::cache_t *varStore_cache = font->num_coords ? varStore.create_cache () : nullptr; +#else + OT::VariationStore::cache_t *varStore_cache = nullptr; +#endif + + for (unsigned int i = 0; i < count; i++) + { + *first_advance = font->em_scale_y (-(int) vmtx.get_advance_with_var_unscaled (*first_glyph, font, varStore_cache)); + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + +#if !defined(HB_NO_VAR) && !defined(HB_NO_OT_FONT_ADVANCE_CACHE) + OT::VariationStore::destroy_cache (varStore_cache); +#endif + } + else + { + hb_font_extents_t font_extents; + font->get_h_extents_with_fallback (&font_extents); + hb_position_t advance = -(font_extents.ascender - font_extents.descender); + + for (unsigned int i = 0; i < count; i++) + { + *first_advance = advance; + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + } + + if (font->y_strength && !font->embolden_in_place) + { + /* Emboldening. */ + hb_position_t y_strength = font->y_scale >= 0 ? font->y_strength : -font->y_strength; + first_advance = orig_first_advance; + for (unsigned int i = 0; i < count; i++) + { + *first_advance += *first_advance ? y_strength : 0; + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + } +} +#endif + +#ifndef HB_NO_VERTICAL +static hb_bool_t +hb_ot_get_glyph_v_origin (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) { const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; - return font->em_scale_x (ot_font->kern->get_h_kerning (left_glyph, right_glyph)); + const hb_ot_face_t *ot_face = ot_font->ot_face; + + *x = font->get_glyph_h_advance (glyph) / 2; + + const OT::VORG &VORG = *ot_face->VORG; + if (VORG.has_data ()) + { + float delta = 0; + +#ifndef HB_NO_VAR + const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx; + const OT::VVAR &VVAR = *vmtx.var_table; + if (font->num_coords) + VVAR.get_vorg_delta_unscaled (glyph, + font->coords, font->num_coords, + &delta); +#endif + + *y = font->em_scalef_y (VORG.get_y_origin (glyph) + delta); + return true; + } + + hb_glyph_extents_t extents = {0}; + if (ot_face->glyf->get_extents (font, glyph, &extents)) + { + const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx; + int tsb = 0; + if (vmtx.get_leading_bearing_with_var_unscaled (font, glyph, &tsb)) + { + *y = extents.y_bearing + font->em_scale_y (tsb); + return true; + } + + hb_font_extents_t font_extents; + font->get_h_extents_with_fallback (&font_extents); + hb_position_t advance = font_extents.ascender - font_extents.descender; + int diff = advance - -extents.height; + *y = extents.y_bearing + (diff >> 1); + return true; + } + + hb_font_extents_t font_extents; + font->get_h_extents_with_fallback (&font_extents); + *y = font_extents.ascender; + + return true; } +#endif static hb_bool_t -hb_ot_get_glyph_extents (hb_font_t *font HB_UNUSED, +hb_ot_get_glyph_extents (hb_font_t *font, void *font_data, hb_codepoint_t glyph, hb_glyph_extents_t *extents, void *user_data HB_UNUSED) { const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; - bool ret = ot_font->glyf->get_extents (glyph, extents); - if (!ret) - ret = ot_font->cbdt->get_extents (glyph, extents); - // TODO Hook up side-bearings variations. - extents->x_bearing = font->em_scale_x (extents->x_bearing); - extents->y_bearing = font->em_scale_y (extents->y_bearing); - extents->width = font->em_scale_x (extents->width); - extents->height = font->em_scale_y (extents->height); + const hb_ot_face_t *ot_face = ot_font->ot_face; + +#if !defined(HB_NO_OT_FONT_BITMAP) && !defined(HB_NO_COLOR) + if (ot_face->sbix->get_extents (font, glyph, extents)) return true; + if (ot_face->CBDT->get_extents (font, glyph, extents)) return true; +#endif +#if !defined(HB_NO_COLOR) && !defined(HB_NO_PAINT) + if (ot_face->COLR->get_extents (font, glyph, extents)) return true; +#endif + if (ot_face->glyf->get_extents (font, glyph, extents)) return true; +#ifndef HB_NO_OT_FONT_CFF + if (ot_face->cff2->get_extents (font, glyph, extents)) return true; + if (ot_face->cff1->get_extents (font, glyph, extents)) return true; +#endif + + return false; +} + +#ifndef HB_NO_OT_FONT_GLYPH_NAMES +static hb_bool_t +hb_ot_get_glyph_name (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t glyph, + char *name, unsigned int size, + void *user_data HB_UNUSED) +{ + const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; + const hb_ot_face_t *ot_face = ot_font->ot_face; + + if (ot_face->post->get_glyph_name (glyph, name, size)) return true; +#ifndef HB_NO_OT_FONT_CFF + if (ot_face->cff1->get_glyph_name (glyph, name, size)) return true; +#endif + return false; +} +static hb_bool_t +hb_ot_get_glyph_from_name (hb_font_t *font HB_UNUSED, + void *font_data, + const char *name, int len, + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; + const hb_ot_face_t *ot_face = ot_font->ot_face; + + if (ot_face->post->get_glyph_from_name (name, len, glyph)) return true; +#ifndef HB_NO_OT_FONT_CFF + if (ot_face->cff1->get_glyph_from_name (name, len, glyph)) return true; +#endif + return false; +} +#endif + +static hb_bool_t +hb_ot_get_font_h_extents (hb_font_t *font, + void *font_data HB_UNUSED, + hb_font_extents_t *metrics, + void *user_data HB_UNUSED) +{ + bool ret = _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER, &metrics->ascender) && + _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER, &metrics->descender) && + _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP, &metrics->line_gap); + + /* Embolden */ + int y_shift = font->y_strength; + if (font->y_scale < 0) y_shift = -y_shift; + metrics->ascender += y_shift; + return ret; } +#ifndef HB_NO_VERTICAL static hb_bool_t -hb_ot_get_glyph_name (hb_font_t *font HB_UNUSED, - void *font_data, - hb_codepoint_t glyph, - char *name, unsigned int size, - void *user_data HB_UNUSED) -{ - const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; - return ot_font->post->get_glyph_name (glyph, name, size); -} - -static hb_bool_t -hb_ot_get_glyph_from_name (hb_font_t *font HB_UNUSED, - void *font_data, - const char *name, int len, - hb_codepoint_t *glyph, - void *user_data HB_UNUSED) -{ - const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; - return ot_font->post->get_glyph_from_name (name, len, glyph); -} - -static hb_bool_t -hb_ot_get_font_h_extents (hb_font_t *font HB_UNUSED, - void *font_data, +hb_ot_get_font_v_extents (hb_font_t *font, + void *font_data HB_UNUSED, hb_font_extents_t *metrics, void *user_data HB_UNUSED) { - const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; - metrics->ascender = font->em_scale_y (ot_font->h_metrics.ascender); - metrics->descender = font->em_scale_y (ot_font->h_metrics.descender); - metrics->line_gap = font->em_scale_y (ot_font->h_metrics.line_gap); - // TODO Hook up variations. - return ot_font->h_metrics.has_font_extents; -} - -static hb_bool_t -hb_ot_get_font_v_extents (hb_font_t *font HB_UNUSED, - void *font_data, - hb_font_extents_t *metrics, - void *user_data HB_UNUSED) -{ - const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; - metrics->ascender = font->em_scale_x (ot_font->v_metrics.ascender); - metrics->descender = font->em_scale_x (ot_font->v_metrics.descender); - metrics->line_gap = font->em_scale_x (ot_font->v_metrics.line_gap); - // TODO Hook up variations. - return ot_font->v_metrics.has_font_extents; -} - -static hb_font_funcs_t *static_ot_funcs = nullptr; - -#ifdef HB_USE_ATEXIT -static -void free_static_ot_funcs (void) -{ - hb_font_funcs_destroy (static_ot_funcs); + return _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_VERTICAL_ASCENDER, &metrics->ascender) && + _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_VERTICAL_DESCENDER, &metrics->descender) && + _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_VERTICAL_LINE_GAP, &metrics->line_gap); } #endif -static hb_font_funcs_t * -_hb_ot_get_font_funcs (void) +#ifndef HB_NO_DRAW +static void +hb_ot_draw_glyph (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, void *draw_data, + void *user_data) { -retry: - hb_font_funcs_t *funcs = (hb_font_funcs_t *) hb_atomic_ptr_get (&static_ot_funcs); + bool embolden = font->x_strength || font->y_strength; + hb_outline_t outline; - if (unlikely (!funcs)) + { // Need draw_session to be destructed before emboldening. + hb_draw_session_t draw_session (embolden ? hb_outline_recording_pen_get_funcs () : draw_funcs, + embolden ? &outline : draw_data, font->slant_xy); + if (!font->face->table.glyf->get_path (font, glyph, draw_session)) +#ifndef HB_NO_CFF + if (!font->face->table.cff2->get_path (font, glyph, draw_session)) + if (!font->face->table.cff1->get_path (font, glyph, draw_session)) +#endif + {} + } + + if (embolden) { - funcs = hb_font_funcs_create (); + float x_shift = font->embolden_in_place ? 0 : (float) font->x_strength / 2; + float y_shift = (float) font->y_strength / 2; + if (font->x_scale < 0) x_shift = -x_shift; + if (font->y_scale < 0) y_shift = -y_shift; + outline.embolden (font->x_strength, font->y_strength, + x_shift, y_shift); + + outline.replay (draw_funcs, draw_data); + } +} +#endif + +#ifndef HB_NO_PAINT +static void +hb_ot_paint_glyph (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + hb_paint_funcs_t *paint_funcs, void *paint_data, + unsigned int palette, + hb_color_t foreground, + void *user_data) +{ +#ifndef HB_NO_COLOR + if (font->face->table.COLR->paint_glyph (font, glyph, paint_funcs, paint_data, palette, foreground)) return; + if (font->face->table.SVG->paint_glyph (font, glyph, paint_funcs, paint_data)) return; +#ifndef HB_NO_OT_FONT_BITMAP + if (font->face->table.CBDT->paint_glyph (font, glyph, paint_funcs, paint_data)) return; + if (font->face->table.sbix->paint_glyph (font, glyph, paint_funcs, paint_data)) return; +#endif +#endif + if (font->face->table.glyf->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return; +#ifndef HB_NO_CFF + if (font->face->table.cff2->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return; + if (font->face->table.cff1->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return; +#endif +} +#endif + +static inline void free_static_ot_funcs (); + +static struct hb_ot_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_t +{ + static hb_font_funcs_t *create () + { + hb_font_funcs_t *funcs = hb_font_funcs_create (); + + hb_font_funcs_set_nominal_glyph_func (funcs, hb_ot_get_nominal_glyph, nullptr, nullptr); + hb_font_funcs_set_nominal_glyphs_func (funcs, hb_ot_get_nominal_glyphs, nullptr, nullptr); + hb_font_funcs_set_variation_glyph_func (funcs, hb_ot_get_variation_glyph, nullptr, nullptr); hb_font_funcs_set_font_h_extents_func (funcs, hb_ot_get_font_h_extents, nullptr, nullptr); - hb_font_funcs_set_font_v_extents_func (funcs, hb_ot_get_font_v_extents, nullptr, nullptr); - hb_font_funcs_set_nominal_glyph_func (funcs, hb_ot_get_nominal_glyph, nullptr, nullptr); - hb_font_funcs_set_variation_glyph_func (funcs, hb_ot_get_variation_glyph, nullptr, nullptr); - hb_font_funcs_set_glyph_h_advance_func (funcs, hb_ot_get_glyph_h_advance, nullptr, nullptr); - hb_font_funcs_set_glyph_v_advance_func (funcs, hb_ot_get_glyph_v_advance, nullptr, nullptr); + hb_font_funcs_set_glyph_h_advances_func (funcs, hb_ot_get_glyph_h_advances, nullptr, nullptr); //hb_font_funcs_set_glyph_h_origin_func (funcs, hb_ot_get_glyph_h_origin, nullptr, nullptr); - //hb_font_funcs_set_glyph_v_origin_func (funcs, hb_ot_get_glyph_v_origin, nullptr, nullptr); - hb_font_funcs_set_glyph_h_kerning_func (funcs, hb_ot_get_glyph_h_kerning, nullptr, nullptr); - //hb_font_funcs_set_glyph_v_kerning_func (funcs, hb_ot_get_glyph_v_kerning, nullptr, nullptr); + +#ifndef HB_NO_VERTICAL + hb_font_funcs_set_font_v_extents_func (funcs, hb_ot_get_font_v_extents, nullptr, nullptr); + hb_font_funcs_set_glyph_v_advances_func (funcs, hb_ot_get_glyph_v_advances, nullptr, nullptr); + hb_font_funcs_set_glyph_v_origin_func (funcs, hb_ot_get_glyph_v_origin, nullptr, nullptr); +#endif + +#ifndef HB_NO_DRAW + hb_font_funcs_set_draw_glyph_func (funcs, hb_ot_draw_glyph, nullptr, nullptr); +#endif + +#ifndef HB_NO_PAINT + hb_font_funcs_set_paint_glyph_func (funcs, hb_ot_paint_glyph, nullptr, nullptr); +#endif + hb_font_funcs_set_glyph_extents_func (funcs, hb_ot_get_glyph_extents, nullptr, nullptr); //hb_font_funcs_set_glyph_contour_point_func (funcs, hb_ot_get_glyph_contour_point, nullptr, nullptr); + +#ifndef HB_NO_OT_FONT_GLYPH_NAMES hb_font_funcs_set_glyph_name_func (funcs, hb_ot_get_glyph_name, nullptr, nullptr); hb_font_funcs_set_glyph_from_name_func (funcs, hb_ot_get_glyph_from_name, nullptr, nullptr); +#endif hb_font_funcs_make_immutable (funcs); - if (!hb_atomic_ptr_cmpexch (&static_ot_funcs, nullptr, funcs)) { - hb_font_funcs_destroy (funcs); - goto retry; - } + hb_atexit (free_static_ot_funcs); -#ifdef HB_USE_ATEXIT - atexit (free_static_ot_funcs); /* First person registers atexit() callback. */ -#endif - }; + return funcs; + } +} static_ot_funcs; - return funcs; +static inline +void free_static_ot_funcs () +{ + static_ot_funcs.free_instance (); +} + +static hb_font_funcs_t * +_hb_ot_get_font_funcs () +{ + return static_ot_funcs.get_unconst (); } /** * hb_ot_font_set_funcs: + * @font: #hb_font_t to work upon + * + * Sets the font functions to use when working with @font. * * Since: 0.9.28 **/ void hb_ot_font_set_funcs (hb_font_t *font) { - hb_ot_font_t *ot_font = _hb_ot_font_create (font->face); + hb_ot_font_t *ot_font = _hb_ot_font_create (font); if (unlikely (!ot_font)) return; @@ -278,3 +650,5 @@ hb_ot_font_set_funcs (hb_font_t *font) ot_font, _hb_ot_font_destroy); } + +#endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-font.h b/src/3rdparty/harfbuzz-ng/src/hb-ot-font.h index 80eaa54b1ad..e7959d1ae28 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-ot-font.h +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-font.h @@ -24,7 +24,7 @@ * Google Author(s): Behdad Esfahbod, Roozbeh Pournader */ -#ifndef HB_OT_H_IN +#if !defined(HB_OT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) #error "Include instead." #endif diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-gasp-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-ot-gasp-table.hh new file mode 100644 index 00000000000..f2a9cad464b --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-gasp-table.hh @@ -0,0 +1,84 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * + * 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. + */ + +#ifndef HB_OT_GASP_TABLE_HH +#define HB_OT_GASP_TABLE_HH + +#include "hb-open-type.hh" +#include "hb-ot-hhea-table.hh" +#include "hb-ot-os2-table.hh" +#include "hb-ot-var-hvar-table.hh" + +/* + * gasp -- Grid-fitting and Scan-conversion Procedure + * https://docs.microsoft.com/en-us/typography/opentype/spec/gasp + */ +#define HB_OT_TAG_gasp HB_TAG('g','a','s','p') + + +namespace OT { + +struct GaspRange +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + HBUINT16 rangeMaxPPEM; /* Upper limit of range, in PPEM */ + HBUINT16 rangeGaspBehavior; + /* Flags describing desired rasterizer behavior. */ + public: + DEFINE_SIZE_STATIC (4); +}; + +struct gasp +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_gasp; + + const GaspRange &get_gasp_range (unsigned int i) const + { return gaspRanges[i]; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + gaspRanges.sanitize (c)); + } + + protected: + HBUINT16 version; /* Version number (set to 1) */ + Array16Of + gaspRanges; /* Number of records to follow + * Sorted by ppem */ + public: + DEFINE_SIZE_ARRAY (4, gaspRanges); +}; + +} /* namespace OT */ + + +#endif /* HB_OT_GASP_TABLE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-glyf-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-ot-glyf-table.hh index 88d3850b6ef..c32ff7636d5 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-ot-glyf-table.hh +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-glyf-table.hh @@ -1,5 +1,7 @@ /* * Copyright © 2015 Google, Inc. + * Copyright © 2019 Adobe Inc. + * Copyright © 2019 Ebrahim Byagowi * * This is part of HarfBuzz, a text shaping library. * @@ -21,160 +23,13 @@ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. * - * Google Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod, Garret Rieger, Roderick Sheeter + * Adobe Author(s): Michiharu Ariza */ #ifndef HB_OT_GLYF_TABLE_HH #define HB_OT_GLYF_TABLE_HH -#include "hb-open-type-private.hh" -#include "hb-ot-head-table.hh" - - -namespace OT { - - -/* - * loca -- Index to Location - */ - -#define HB_OT_TAG_loca HB_TAG('l','o','c','a') - - -struct loca -{ - friend struct glyf; - - static const hb_tag_t tableTag = HB_OT_TAG_loca; - - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (true); - } - - protected: - UINT8 dataX[VAR]; /* Location data. */ - DEFINE_SIZE_ARRAY (0, dataX); -}; - - -/* - * glyf -- TrueType Glyph Data - */ - -#define HB_OT_TAG_glyf HB_TAG('g','l','y','f') - - -struct glyf -{ - static const hb_tag_t tableTag = HB_OT_TAG_glyf; - - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - /* We don't check for anything specific here. The users of the - * struct do all the hard work... */ - return_trace (true); - } - - struct GlyphHeader - { - INT16 numberOfContours; /* If the number of contours is - * greater than or equal to zero, - * this is a simple glyph; if negative, - * this is a composite glyph. */ - FWORD xMin; /* Minimum x for coordinate data. */ - FWORD yMin; /* Minimum y for coordinate data. */ - FWORD xMax; /* Maximum x for coordinate data. */ - FWORD yMax; /* Maximum y for coordinate data. */ - - DEFINE_SIZE_STATIC (10); - }; - - struct accelerator_t - { - inline void init (hb_face_t *face) - { - hb_blob_t *head_blob = Sanitizer::sanitize (face->reference_table (HB_OT_TAG_head)); - const head *head_table = Sanitizer::lock_instance (head_blob); - if ((unsigned int) head_table->indexToLocFormat > 1 || head_table->glyphDataFormat != 0) - { - /* Unknown format. Leave num_glyphs=0, that takes care of disabling us. */ - hb_blob_destroy (head_blob); - return; - } - short_offset = 0 == head_table->indexToLocFormat; - hb_blob_destroy (head_blob); - - loca_blob = Sanitizer::sanitize (face->reference_table (HB_OT_TAG_loca)); - loca_table = Sanitizer::lock_instance (loca_blob); - glyf_blob = Sanitizer::sanitize (face->reference_table (HB_OT_TAG_glyf)); - glyf_table = Sanitizer::lock_instance (glyf_blob); - - num_glyphs = MAX (1u, hb_blob_get_length (loca_blob) / (short_offset ? 2 : 4)) - 1; - glyf_len = hb_blob_get_length (glyf_blob); - } - - inline void fini (void) - { - hb_blob_destroy (loca_blob); - hb_blob_destroy (glyf_blob); - } - - inline bool get_extents (hb_codepoint_t glyph, - hb_glyph_extents_t *extents) const - { - if (unlikely (glyph >= num_glyphs)) - return false; - - unsigned int start_offset, end_offset; - if (short_offset) - { - const UINT16 *offsets = (const UINT16 *) loca_table->dataX; - start_offset = 2 * offsets[glyph]; - end_offset = 2 * offsets[glyph + 1]; - } - else - { - const UINT32 *offsets = (const UINT32 *) loca_table->dataX; - start_offset = offsets[glyph]; - end_offset = offsets[glyph + 1]; - } - - if (start_offset > end_offset || end_offset > glyf_len) - return false; - - if (end_offset - start_offset < GlyphHeader::static_size) - return true; /* Empty glyph; zero extents. */ - - const GlyphHeader &glyph_header = StructAtOffset (glyf_table, start_offset); - - extents->x_bearing = MIN (glyph_header.xMin, glyph_header.xMax); - extents->y_bearing = MAX (glyph_header.yMin, glyph_header.yMax); - extents->width = MAX (glyph_header.xMin, glyph_header.xMax) - extents->x_bearing; - extents->height = MIN (glyph_header.yMin, glyph_header.yMax) - extents->y_bearing; - - return true; - } - - private: - bool short_offset; - unsigned int num_glyphs; - const loca *loca_table; - const glyf *glyf_table; - hb_blob_t *loca_blob; - hb_blob_t *glyf_blob; - unsigned int glyf_len; - }; - - protected: - UINT8 dataX[VAR]; /* Glyphs data. */ - - DEFINE_SIZE_ARRAY (0, dataX); -}; - -} /* namespace OT */ - +#include "OT/glyf/glyf.hh" #endif /* HB_OT_GLYF_TABLE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-hdmx-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-ot-hdmx-table.hh new file mode 100644 index 00000000000..cbcf6f5f22f --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-hdmx-table.hh @@ -0,0 +1,175 @@ +/* + * 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 + */ + +#ifndef HB_OT_HDMX_TABLE_HH +#define HB_OT_HDMX_TABLE_HH + +#include "hb-open-type.hh" + +/* + * hdmx -- Horizontal Device Metrics + * https://docs.microsoft.com/en-us/typography/opentype/spec/hdmx + */ +#define HB_OT_TAG_hdmx HB_TAG('h','d','m','x') + + +namespace OT { + + +struct DeviceRecord +{ + static unsigned int get_size (unsigned count) + { return hb_ceil_to_4 (min_size + count * HBUINT8::static_size); } + + template + bool serialize (hb_serialize_context_t *c, + unsigned pixelSize, + Iterator it, + const hb_vector_t new_to_old_gid_list, + unsigned num_glyphs) + { + TRACE_SERIALIZE (this); + + if (unlikely (!c->extend (this, num_glyphs))) return_trace (false); + + this->pixelSize = pixelSize; + this->maxWidth = + + it + | hb_reduce (hb_max, 0u); + + for (auto &_ : new_to_old_gid_list) + widthsZ[_.first] = *it++; + + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c, unsigned sizeDeviceRecord) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + c->check_range (this, sizeDeviceRecord))); + } + + HBUINT8 pixelSize; /* Pixel size for following widths (as ppem). */ + HBUINT8 maxWidth; /* Maximum width. */ + UnsizedArrayOf widthsZ; /* Array of widths (numGlyphs is from the 'maxp' table). */ + public: + DEFINE_SIZE_UNBOUNDED (2); +}; + + +struct hdmx +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_hdmx; + + unsigned int get_size () const + { return min_size + numRecords * sizeDeviceRecord; } + + template + bool serialize (hb_serialize_context_t *c, + unsigned version, + Iterator it, + const hb_vector_t &new_to_old_gid_list, + unsigned num_glyphs) + { + TRACE_SERIALIZE (this); + + if (unlikely (!c->extend_min ((*this)))) return_trace (false); + + this->version = version; + this->numRecords = it.len (); + this->sizeDeviceRecord = DeviceRecord::get_size (num_glyphs); + + for (const hb_item_type& _ : +it) + c->start_embed ()->serialize (c, _.first, _.second, new_to_old_gid_list, num_glyphs); + + return_trace (c->successful ()); + } + + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + auto *hdmx_prime = c->serializer->start_embed (); + + unsigned num_input_glyphs = get_num_glyphs (); + auto it = + + hb_range ((unsigned) numRecords) + | hb_map ([c, num_input_glyphs, this] (unsigned _) + { + const DeviceRecord *device_record = + &StructAtOffset (&firstDeviceRecord, + _ * sizeDeviceRecord); + auto row = + + hb_iter (c->plan->new_to_old_gid_list) + | hb_map ([num_input_glyphs, device_record] (hb_codepoint_pair_t _) + { + return device_record->widthsZ.as_array (num_input_glyphs) [_.second]; + }) + ; + return hb_pair ((unsigned) device_record->pixelSize, +row); + }) + ; + + hdmx_prime->serialize (c->serializer, version, it, + c->plan->new_to_old_gid_list, + c->plan->num_output_glyphs ()); + return_trace (true); + } + + unsigned get_num_glyphs () const + { + return sizeDeviceRecord - DeviceRecord::min_size; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + !hb_unsigned_mul_overflows (numRecords, sizeDeviceRecord) && + min_size + numRecords * sizeDeviceRecord > numRecords * sizeDeviceRecord && + sizeDeviceRecord >= DeviceRecord::min_size && + c->check_range (this, get_size ())); + } + + protected: + HBUINT16 version; /* Table version number (0) */ + HBUINT16 numRecords; /* Number of device records. */ + HBUINT32 sizeDeviceRecord; + /* Size of a device record, 32-bit aligned. */ + DeviceRecord firstDeviceRecord; + /* Array of device records. */ + public: + DEFINE_SIZE_MIN (8); +}; + +} /* namespace OT */ + + +#endif /* HB_OT_HDMX_TABLE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-head-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-ot-head-table.hh index dd4349ef85a..770cf52d173 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-ot-head-table.hh +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-head-table.hh @@ -29,30 +29,77 @@ #ifndef HB_OT_HEAD_TABLE_HH #define HB_OT_HEAD_TABLE_HH -#include "hb-open-type-private.hh" +#include "hb-open-type.hh" + +/* + * head -- Font Header + * https://docs.microsoft.com/en-us/typography/opentype/spec/head + */ +#define HB_OT_TAG_head HB_TAG('h','e','a','d') namespace OT { -/* - * head -- Font Header - */ - -#define HB_OT_TAG_head HB_TAG('h','e','a','d') - struct head { - static const hb_tag_t tableTag = HB_OT_TAG_head; + friend struct OpenTypeOffsetTable; - inline unsigned int get_upem (void) const + static constexpr hb_tag_t tableTag = HB_OT_TAG_head; + + unsigned int get_upem () const { unsigned int upem = unitsPerEm; /* If no valid head table found, assume 1000, which matches typical Type1 usage. */ return 16 <= upem && upem <= 16384 ? upem : 1000; } - inline bool sanitize (hb_sanitize_context_t *c) const + bool serialize (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + return_trace ((bool) c->embed (this)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + head *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + if (c->plan->normalized_coords) + { + if (unlikely (!c->serializer->check_assign (out->xMin, c->plan->head_maxp_info.xMin, + HB_SERIALIZE_ERROR_INT_OVERFLOW))) + return_trace (false); + if (unlikely (!c->serializer->check_assign (out->xMax, c->plan->head_maxp_info.xMax, + HB_SERIALIZE_ERROR_INT_OVERFLOW))) + return_trace (false); + if (unlikely (!c->serializer->check_assign (out->yMin, c->plan->head_maxp_info.yMin, + HB_SERIALIZE_ERROR_INT_OVERFLOW))) + return_trace (false); + if (unlikely (!c->serializer->check_assign (out->yMax, c->plan->head_maxp_info.yMax, + HB_SERIALIZE_ERROR_INT_OVERFLOW))) + return_trace (false); + } + return_trace (true); + } + + enum mac_style_flag_t { + BOLD = 1u<<0, + ITALIC = 1u<<1, + UNDERLINE = 1u<<2, + OUTLINE = 1u<<3, + SHADOW = 1u<<4, + CONDENSED = 1u<<5, + EXPANDED = 1u<<6, + }; + + bool is_bold () const { return macStyle & BOLD; } + bool is_italic () const { return macStyle & ITALIC; } + bool is_condensed () const { return macStyle & CONDENSED; } + bool is_expanded () const { return macStyle & EXPANDED; } + + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && @@ -64,11 +111,12 @@ struct head FixedVersion<>version; /* Version of the head table--currently * 0x00010000u for version 1.0. */ FixedVersion<>fontRevision; /* Set by font manufacturer. */ - UINT32 checkSumAdjustment; /* To compute: set it to 0, sum the - * entire font as UINT32, then store + HBUINT32 checkSumAdjustment; /* To compute: set it to 0, sum the + * entire font as HBUINT32, then store * 0xB1B0AFBAu - sum. */ - UINT32 magicNumber; /* Set to 0x5F0F3CF5u. */ - UINT16 flags; /* Bit 0: Baseline for font at y=0; + HBUINT32 magicNumber; /* Set to 0x5F0F3CF5u. */ + public: + HBUINT16 flags; /* Bit 0: Baseline for font at y=0; * Bit 1: Left sidebearing point at x=0; * Bit 2: Instructions may depend on point size; * Bit 3: Force ppem to integer values for all @@ -76,7 +124,6 @@ struct head * ppem sizes if this bit is clear; * Bit 4: Instructions may alter advance width * (the advance widths might not scale linearly); - * Bits 5-10: These should be set according to * Apple's specification. However, they are not * implemented in OpenType. @@ -96,7 +143,6 @@ struct head * contains any strong right-to-left glyphs. * Bit 10: This bit should be set if the font * contains Indic-style rearrangement effects. - * Bit 11: Font data is 'lossless,' as a result * of having been compressed and decompressed * with the Agfa MicroType Express engine. @@ -114,18 +160,21 @@ struct head * encoded in the cmap subtables represent proper * support for those code points. * Bit 15: Reserved, set to 0. */ - UINT16 unitsPerEm; /* Valid range is from 16 to 16384. This value + protected: + HBUINT16 unitsPerEm; /* Valid range is from 16 to 16384. This value * should be a power of 2 for fonts that have * TrueType outlines. */ LONGDATETIME created; /* Number of seconds since 12:00 midnight, January 1, 1904. 64-bit integer */ LONGDATETIME modified; /* Number of seconds since 12:00 midnight, January 1, 1904. 64-bit integer */ - INT16 xMin; /* For all glyph bounding boxes. */ - INT16 yMin; /* For all glyph bounding boxes. */ - INT16 xMax; /* For all glyph bounding boxes. */ - INT16 yMax; /* For all glyph bounding boxes. */ - UINT16 macStyle; /* Bit 0: Bold (if set to 1); + public: + HBINT16 xMin; /* For all glyph bounding boxes. */ + HBINT16 yMin; /* For all glyph bounding boxes. */ + HBINT16 xMax; /* For all glyph bounding boxes. */ + HBINT16 yMax; /* For all glyph bounding boxes. */ + protected: + HBUINT16 macStyle; /* Bit 0: Bold (if set to 1); * Bit 1: Italic (if set to 1) * Bit 2: Underline (if set to 1) * Bit 3: Outline (if set to 1) @@ -133,16 +182,16 @@ struct head * Bit 5: Condensed (if set to 1) * Bit 6: Extended (if set to 1) * Bits 7-15: Reserved (set to 0). */ - UINT16 lowestRecPPEM; /* Smallest readable size in pixels. */ - INT16 fontDirectionHint; /* Deprecated (Set to 2). + HBUINT16 lowestRecPPEM; /* Smallest readable size in pixels. */ + HBINT16 fontDirectionHint; /* Deprecated (Set to 2). * 0: Fully mixed directional glyphs; * 1: Only strongly left to right; * 2: Like 1 but also contains neutrals; * -1: Only strongly right to left; * -2: Like -1 but also contains neutrals. */ public: - INT16 indexToLocFormat; /* 0 for short offsets, 1 for long. */ - INT16 glyphDataFormat; /* 0 for current format. */ + HBUINT16 indexToLocFormat; /* 0 for short offsets, 1 for long. */ + HBUINT16 glyphDataFormat; /* 0 for current format. */ DEFINE_SIZE_STATIC (54); }; diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-hhea-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-ot-hhea-table.hh index dca014148ea..d9c9bd35377 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-ot-hhea-table.hh +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-hhea-table.hh @@ -27,68 +27,74 @@ #ifndef HB_OT_HHEA_TABLE_HH #define HB_OT_HHEA_TABLE_HH -#include "hb-open-type-private.hh" +#include "hb-open-type.hh" + +/* + * hhea -- Horizontal Header + * https://docs.microsoft.com/en-us/typography/opentype/spec/hhea + * vhea -- Vertical Header + * https://docs.microsoft.com/en-us/typography/opentype/spec/vhea + */ +#define HB_OT_TAG_hhea HB_TAG('h','h','e','a') +#define HB_OT_TAG_vhea HB_TAG('v','h','e','a') namespace OT { -/* - * hhea -- The Horizontal Header Table - * vhea -- The Vertical Header Table - */ - -#define HB_OT_TAG_hhea HB_TAG('h','h','e','a') -#define HB_OT_TAG_vhea HB_TAG('v','h','e','a') - - +template struct _hea { - inline bool sanitize (hb_sanitize_context_t *c) const + bool has_data () const { return version.major; } + + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && likely (version.major == 1)); } public: - FixedVersion<>version; /* 0x00010000u for version 1.0. */ - FWORD ascender; /* Typographic ascent. */ - FWORD descender; /* Typographic descent. */ - FWORD lineGap; /* Typographic line gap. */ - UFWORD advanceMax; /* Maximum advance width/height value in - * metrics table. */ - FWORD minLeadingBearing; /* Minimum left/top sidebearing value in - * metrics table. */ - FWORD minTrailingBearing; /* Minimum right/bottom sidebearing value; - * calculated as Min(aw - lsb - - * (xMax - xMin)) for horizontal. */ - FWORD maxExtent; /* horizontal: Max(lsb + (xMax - xMin)), - * vertical: minLeadingBearing+(yMax-yMin). */ - INT16 caretSlopeRise; /* Used to calculate the slope of the - * cursor (rise/run); 1 for vertical caret, - * 0 for horizontal.*/ - INT16 caretSlopeRun; /* 0 for vertical caret, 1 for horizontal. */ - INT16 caretOffset; /* The amount by which a slanted - * highlight on a glyph needs - * to be shifted to produce the - * best appearance. Set to 0 for - * non-slanted fonts. */ - INT16 reserved1; /* Set to 0. */ - INT16 reserved2; /* Set to 0. */ - INT16 reserved3; /* Set to 0. */ - INT16 reserved4; /* Set to 0. */ - INT16 metricDataFormat; /* 0 for current format. */ - UINT16 numberOfLongMetrics; /* Number of LongMetric entries in metric - * table. */ + FixedVersion<>version; /* 0x00010000u for version 1.0. */ + FWORD ascender; /* Typographic ascent. */ + FWORD descender; /* Typographic descent. */ + FWORD lineGap; /* Typographic line gap. */ + UFWORD advanceMax; /* Maximum advance width/height value in + * metrics table. */ + FWORD minLeadingBearing; + /* Minimum left/top sidebearing value in + * metrics table. */ + FWORD minTrailingBearing; + /* Minimum right/bottom sidebearing value; + * calculated as Min(aw - lsb - + * (xMax - xMin)) for horizontal. */ + FWORD maxExtent; /* horizontal: Max(lsb + (xMax - xMin)), + * vertical: minLeadingBearing+(yMax-yMin). */ + HBINT16 caretSlopeRise; /* Used to calculate the slope of the + * cursor (rise/run); 1 for vertical caret, + * 0 for horizontal.*/ + HBINT16 caretSlopeRun; /* 0 for vertical caret, 1 for horizontal. */ + HBINT16 caretOffset; /* The amount by which a slanted + * highlight on a glyph needs + * to be shifted to produce the + * best appearance. Set to 0 for + * non-slanted fonts. */ + HBINT16 reserved1; /* Set to 0. */ + HBINT16 reserved2; /* Set to 0. */ + HBINT16 reserved3; /* Set to 0. */ + HBINT16 reserved4; /* Set to 0. */ + HBINT16 metricDataFormat;/* 0 for current format. */ + HBUINT16 numberOfLongMetrics; + /* Number of LongMetric entries in metric + * table. */ public: DEFINE_SIZE_STATIC (36); }; -struct hhea : _hea { - static const hb_tag_t tableTag = HB_OT_TAG_hhea; +struct hhea : _hea { + static constexpr hb_tag_t tableTag = HB_OT_TAG_hhea; }; -struct vhea : _hea { - static const hb_tag_t tableTag = HB_OT_TAG_vhea; +struct vhea : _hea { + static constexpr hb_tag_t tableTag = HB_OT_TAG_vhea; }; diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-hmtx-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-ot-hmtx-table.hh index e710aee42ec..89640b43f15 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-ot-hmtx-table.hh +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-hmtx-table.hh @@ -21,42 +21,55 @@ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. * - * Google Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod, Roderick Sheeter */ #ifndef HB_OT_HMTX_TABLE_HH #define HB_OT_HMTX_TABLE_HH -#include "hb-open-type-private.hh" +#include "hb-open-type.hh" +#include "hb-ot-maxp-table.hh" #include "hb-ot-hhea-table.hh" -#include "hb-ot-os2-table.hh" #include "hb-ot-var-hvar-table.hh" +#include "hb-ot-var-mvar-table.hh" +#include "hb-ot-metrics.hh" + +/* + * hmtx -- Horizontal Metrics + * https://docs.microsoft.com/en-us/typography/opentype/spec/hmtx + * vmtx -- Vertical Metrics + * https://docs.microsoft.com/en-us/typography/opentype/spec/vmtx + */ +#define HB_OT_TAG_hmtx HB_TAG('h','m','t','x') +#define HB_OT_TAG_vmtx HB_TAG('v','m','t','x') + + +HB_INTERNAL bool +_glyf_get_leading_bearing_with_var_unscaled (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical, int *lsb); + +HB_INTERNAL unsigned +_glyf_get_advance_with_var_unscaled (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical); + +HB_INTERNAL bool +_glyf_get_leading_bearing_without_var_unscaled (hb_face_t *face, hb_codepoint_t gid, bool is_vertical, int *lsb); namespace OT { -/* - * hmtx -- The Horizontal Metrics Table - * vmtx -- The Vertical Metrics Table - */ - -#define HB_OT_TAG_hmtx HB_TAG('h','m','t','x') -#define HB_OT_TAG_vmtx HB_TAG('v','m','t','x') - - struct LongMetric { UFWORD advance; /* Advance width/height. */ - FWORD lsb; /* Leading (left/top) side bearing. */ + FWORD sb; /* Leading (left/top) side bearing. */ public: DEFINE_SIZE_STATIC (4); }; -template + +template struct hmtxvmtx { - inline bool sanitize (hb_sanitize_context_t *c) const + bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const { TRACE_SANITIZE (this); /* We don't check for anything specific here. The users of the @@ -64,143 +77,398 @@ struct hmtxvmtx return_trace (true); } + const hb_hashmap_t>* get_mtx_map (const hb_subset_plan_t *plan) const + { return T::is_horizontal ? &plan->hmtx_map : &plan->vmtx_map; } + + bool subset_update_header (hb_subset_context_t *c, + unsigned int num_hmetrics, + const hb_hashmap_t> *mtx_map, + const hb_vector_t &bounds_vec) const + { + hb_blob_t *src_blob = hb_sanitize_context_t ().reference_table (c->plan->source, H::tableTag); + hb_blob_t *dest_blob = hb_blob_copy_writable_or_fail (src_blob); + hb_blob_destroy (src_blob); + + if (unlikely (!dest_blob)) { + return false; + } + + unsigned int length; + H *table = (H *) hb_blob_get_data (dest_blob, &length); + c->serializer->check_assign (table->numberOfLongMetrics, num_hmetrics, HB_SERIALIZE_ERROR_INT_OVERFLOW); + +#ifndef HB_NO_VAR + if (c->plan->normalized_coords) + { + auto &MVAR = *c->plan->source->table.MVAR; + if (T::is_horizontal) + { + HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_HORIZONTAL_CARET_RISE, caretSlopeRise); + HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_HORIZONTAL_CARET_RUN, caretSlopeRun); + HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_HORIZONTAL_CARET_OFFSET, caretOffset); + } + else + { + HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_VERTICAL_CARET_RISE, caretSlopeRise); + HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_VERTICAL_CARET_RUN, caretSlopeRun); + HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_VERTICAL_CARET_OFFSET, caretOffset); + } + + bool empty = true; + int min_lsb = 0x7FFF; + int min_rsb = 0x7FFF; + int max_extent = -0x7FFF; + unsigned max_adv = 0; + for (const auto _ : *mtx_map) + { + hb_codepoint_t gid = _.first; + unsigned adv = _.second.first; + int lsb = _.second.second; + max_adv = hb_max (max_adv, adv); + + if (bounds_vec[gid] != 0xFFFFFFFF) + { + empty = false; + unsigned bound_width = bounds_vec[gid]; + int rsb = adv - lsb - bound_width; + int extent = lsb + bound_width; + min_lsb = hb_min (min_lsb, lsb); + min_rsb = hb_min (min_rsb, rsb); + max_extent = hb_max (max_extent, extent); + } + } + + table->advanceMax = max_adv; + if (!empty) + { + table->minLeadingBearing = min_lsb; + table->minTrailingBearing = min_rsb; + table->maxExtent = max_extent; + } + } +#endif + + bool result = c->plan->add_table (H::tableTag, dest_blob); + hb_blob_destroy (dest_blob); + + return result; + } + + template + void serialize (hb_serialize_context_t *c, + Iterator it, + const hb_vector_t new_to_old_gid_list, + unsigned num_long_metrics, + unsigned total_num_metrics) + { + LongMetric* long_metrics = c->allocate_size (num_long_metrics * LongMetric::static_size); + FWORD* short_metrics = c->allocate_size ((total_num_metrics - num_long_metrics) * FWORD::static_size); + if (!long_metrics || !short_metrics) return; + + short_metrics -= num_long_metrics; + + for (auto _ : new_to_old_gid_list) + { + hb_codepoint_t gid = _.first; + auto mtx = *it++; + + if (gid < num_long_metrics) + { + LongMetric& lm = long_metrics[gid]; + lm.advance = mtx.first; + lm.sb = mtx.second; + } + // TODO(beyond-64k): This assumes that maxp.numGlyphs is 0xFFFF. + else if (gid < 0x10000u) + short_metrics[gid] = mtx.second; + else + ((UFWORD*) short_metrics)[gid] = mtx.first; + } + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + auto *table_prime = c->serializer->start_embed (); + + accelerator_t _mtx (c->plan->source); + unsigned num_long_metrics; + const hb_hashmap_t> *mtx_map = get_mtx_map (c->plan); + { + /* Determine num_long_metrics to encode. */ + auto& plan = c->plan; + + // TODO Don't consider retaingid holes here. + + num_long_metrics = hb_min (plan->num_output_glyphs (), 0xFFFFu); + unsigned int last_advance = get_new_gid_advance_unscaled (plan, mtx_map, num_long_metrics - 1, _mtx); + while (num_long_metrics > 1 && + last_advance == get_new_gid_advance_unscaled (plan, mtx_map, num_long_metrics - 2, _mtx)) + { + num_long_metrics--; + } + } + + auto it = + + hb_iter (c->plan->new_to_old_gid_list) + | hb_map ([c, &_mtx, mtx_map] (hb_codepoint_pair_t _) + { + hb_codepoint_t new_gid = _.first; + hb_codepoint_t old_gid = _.second; + + hb_pair_t *v = nullptr; + if (!mtx_map->has (new_gid, &v)) + { + int lsb = 0; + if (!_mtx.get_leading_bearing_without_var_unscaled (old_gid, &lsb)) + (void) _glyf_get_leading_bearing_without_var_unscaled (c->plan->source, old_gid, !T::is_horizontal, &lsb); + return hb_pair (_mtx.get_advance_without_var_unscaled (old_gid), +lsb); + } + return *v; + }) + ; + + table_prime->serialize (c->serializer, + it, + c->plan->new_to_old_gid_list, + num_long_metrics, + c->plan->num_output_glyphs ()); + + if (unlikely (c->serializer->in_error ())) + return_trace (false); + + // Amend header num hmetrics + if (unlikely (!subset_update_header (c, num_long_metrics, mtx_map, + T::is_horizontal ? c->plan->bounds_width_vec : c->plan->bounds_height_vec))) + return_trace (false); + + return_trace (true); + } + struct accelerator_t { - inline void init (hb_face_t *face, - unsigned int default_advance_ = 0) + friend struct hmtxvmtx; + + accelerator_t (hb_face_t *face) { - default_advance = default_advance_ ? default_advance_ : face->get_upem (); + table = hb_sanitize_context_t ().reference_table (face, T::tableTag); + var_table = hb_sanitize_context_t ().reference_table (face, T::variationsTag); - bool got_font_extents = false; - if (T::os2Tag) - { - hb_blob_t *os2_blob = Sanitizer::sanitize (face->reference_table (T::os2Tag)); - const os2 *os2_table = Sanitizer::lock_instance (os2_blob); -#define USE_TYPO_METRICS (1u<<7) - if (0 != (os2_table->fsSelection & USE_TYPO_METRICS)) - { - ascender = os2_table->sTypoAscender; - descender = os2_table->sTypoDescender; - line_gap = os2_table->sTypoLineGap; - got_font_extents = (ascender | descender) != 0; - } - hb_blob_destroy (os2_blob); - } + default_advance = T::is_horizontal ? hb_face_get_upem (face) / 2 : hb_face_get_upem (face); - hb_blob_t *_hea_blob = Sanitizer<_hea>::sanitize (face->reference_table (T::headerTag)); - const _hea *_hea_table = Sanitizer<_hea>::lock_instance (_hea_blob); - num_advances = _hea_table->numberOfLongMetrics; - if (!got_font_extents) - { - ascender = _hea_table->ascender; - descender = _hea_table->descender; - line_gap = _hea_table->lineGap; - got_font_extents = (ascender | descender) != 0; - } - hb_blob_destroy (_hea_blob); + /* Populate count variables and sort them out as we go */ - has_font_extents = got_font_extents; + unsigned int len = table.get_length (); + if (len & 1) + len--; - blob = Sanitizer::sanitize (face->reference_table (T::tableTag)); + num_long_metrics = T::is_horizontal ? + face->table.hhea->numberOfLongMetrics : +#ifndef HB_NO_VERTICAL + face->table.vhea->numberOfLongMetrics +#else + 0 +#endif + ; + if (unlikely (num_long_metrics * 4 > len)) + num_long_metrics = len / 4; + len -= num_long_metrics * 4; - /* Cap num_metrics() and num_advances() based on table length. */ - unsigned int len = hb_blob_get_length (blob); - if (unlikely (num_advances * 4 > len)) - num_advances = len / 4; - num_metrics = num_advances + (len - 4 * num_advances) / 2; + num_bearings = face->table.maxp->get_num_glyphs (); - /* We MUST set num_metrics to zero if num_advances is zero. + if (unlikely (num_bearings < num_long_metrics)) + num_bearings = num_long_metrics; + if (unlikely ((num_bearings - num_long_metrics) * 2 > len)) + num_bearings = num_long_metrics + len / 2; + len -= (num_bearings - num_long_metrics) * 2; + + /* We MUST set num_bearings to zero if num_long_metrics is zero. * Our get_advance() depends on that. */ + if (unlikely (!num_long_metrics)) + num_bearings = num_long_metrics = 0; + + num_advances = num_bearings + len / 2; + num_glyphs = face->get_num_glyphs (); + if (num_glyphs < num_advances) + num_glyphs = num_advances; + } + ~accelerator_t () + { + table.destroy (); + var_table.destroy (); + } + + bool has_data () const { return (bool) num_bearings; } + + bool get_leading_bearing_without_var_unscaled (hb_codepoint_t glyph, + int *lsb) const + { + if (glyph < num_long_metrics) + { + *lsb = table->longMetricZ[glyph].sb; + return true; + } + + if (unlikely (glyph >= num_bearings)) + return false; + + const FWORD *bearings = (const FWORD *) &table->longMetricZ[num_long_metrics]; + *lsb = bearings[glyph - num_long_metrics]; + return true; + } + + bool get_leading_bearing_with_var_unscaled (hb_font_t *font, + hb_codepoint_t glyph, + int *lsb) const + { + if (!font->num_coords) + return get_leading_bearing_without_var_unscaled (glyph, lsb); + +#ifndef HB_NO_VAR + float delta; + if (var_table->get_lsb_delta_unscaled (glyph, font->coords, font->num_coords, &delta) && + get_leading_bearing_without_var_unscaled (glyph, lsb)) + { + *lsb += roundf (delta); + return true; + } + + return _glyf_get_leading_bearing_with_var_unscaled (font, glyph, T::tableTag == HB_OT_TAG_vmtx, lsb); +#else + return false; +#endif + } + + unsigned int get_advance_without_var_unscaled (hb_codepoint_t glyph) const + { + /* OpenType case. */ + if (glyph < num_bearings) + return table->longMetricZ[hb_min (glyph, (uint32_t) num_long_metrics - 1)].advance; + + /* If num_advances is zero, it means we don't have the metrics table + * for this direction: return default advance. Otherwise, there's a + * well-defined answer. */ if (unlikely (!num_advances)) - { - num_metrics = num_advances = 0; - hb_blob_destroy (blob); - blob = hb_blob_get_empty (); - } - table = Sanitizer::lock_instance (blob); + return default_advance; - var_blob = Sanitizer::sanitize (face->reference_table (T::variationsTag)); - var_table = Sanitizer::lock_instance (var_blob); +#ifdef HB_NO_BEYOND_64K + return 0; +#endif + + if (unlikely (glyph >= num_glyphs)) + return 0; + + /* num_bearings <= glyph < num_glyphs; + * num_bearings <= num_advances */ + + if (num_bearings == num_advances) + return get_advance_without_var_unscaled (num_bearings - 1); + + const FWORD *bearings = (const FWORD *) &table->longMetricZ[num_long_metrics]; + const UFWORD *advances = (const UFWORD *) &bearings[num_bearings - num_long_metrics]; + + return advances[hb_min (glyph - num_bearings, num_advances - num_bearings - 1)]; } - inline void fini (void) + unsigned get_advance_with_var_unscaled (hb_codepoint_t glyph, + hb_font_t *font, + VariationStore::cache_t *store_cache = nullptr) const { - hb_blob_destroy (blob); - hb_blob_destroy (var_blob); + unsigned int advance = get_advance_without_var_unscaled (glyph); + +#ifndef HB_NO_VAR + if (unlikely (glyph >= num_bearings) || !font->num_coords) + return advance; + + if (var_table.get_length ()) + return advance + roundf (var_table->get_advance_delta_unscaled (glyph, + font->coords, font->num_coords, + store_cache)); + + return _glyf_get_advance_with_var_unscaled (font, glyph, T::tableTag == HB_OT_TAG_vmtx); +#else + return advance; +#endif } - inline unsigned int get_advance (hb_codepoint_t glyph, - hb_font_t *font) const - { - if (unlikely (glyph >= num_metrics)) - { - /* If num_metrics is zero, it means we don't have the metrics table - * for this direction: return default advance. Otherwise, it means that the - * glyph index is out of bound: return zero. */ - if (num_metrics) - return 0; - else - return default_advance; - } + protected: + // 0 <= num_long_metrics <= num_bearings <= num_advances <= num_glyphs + unsigned num_long_metrics; + unsigned num_bearings; + unsigned num_advances; + unsigned num_glyphs; - return table->longMetric[MIN (glyph, (uint32_t) num_advances - 1)].advance - + var_table->get_advance_var (glyph, font->coords, font->num_coords); // TODO Optimize?! - } - - public: - bool has_font_extents; - unsigned short ascender; - unsigned short descender; - unsigned short line_gap; - - private: - unsigned int num_metrics; - unsigned int num_advances; unsigned int default_advance; - const hmtxvmtx *table; - hb_blob_t *blob; - const HVARVVAR *var_table; - hb_blob_t *var_blob; + public: + hb_blob_ptr_t table; + hb_blob_ptr_t var_table; }; + /* get advance: when no variations, call get_advance_without_var_unscaled. + * when there're variations, get advance value from mtx_map in subset_plan*/ + unsigned get_new_gid_advance_unscaled (const hb_subset_plan_t *plan, + const hb_hashmap_t> *mtx_map, + unsigned new_gid, + const accelerator_t &_mtx) const + { + if (mtx_map->is_empty ()) + { + hb_codepoint_t old_gid = 0; + return plan->old_gid_for_new_gid (new_gid, &old_gid) ? + _mtx.get_advance_without_var_unscaled (old_gid) : 0; + } + return mtx_map->get (new_gid).first; + } + protected: - LongMetric longMetric[VAR]; /* Paired advance width and leading - * bearing values for each glyph. The - * value numOfHMetrics comes from - * the 'hhea' table. If the font is - * monospaced, only one entry need - * be in the array, but that entry is - * required. The last entry applies to - * all subsequent glyphs. */ - FWORD leadingBearingX[VAR]; /* Here the advance is assumed - * to be the same as the advance - * for the last entry above. The - * number of entries in this array is - * derived from numGlyphs (from 'maxp' - * table) minus numberOfLongMetrics. - * This generally is used with a run - * of monospaced glyphs (e.g., Kanji - * fonts or Courier fonts). Only one - * run is allowed and it must be at - * the end. This allows a monospaced - * font to vary the side bearing - * values for each glyph. */ + UnsizedArrayOf + longMetricZ; /* Paired advance width and leading + * bearing values for each glyph. The + * value numOfHMetrics comes from + * the 'hhea' table. If the font is + * monospaced, only one entry need + * be in the array, but that entry is + * required. The last entry applies to + * all subsequent glyphs. */ +/*UnsizedArrayOf leadingBearingX;*/ + /* Here the advance is assumed + * to be the same as the advance + * for the last entry above. The + * number of entries in this array is + * derived from numGlyphs (from 'maxp' + * table) minus numberOfLongMetrics. + * This generally is used with a run + * of monospaced glyphs (e.g., Kanji + * fonts or Courier fonts). Only one + * run is allowed and it must be at + * the end. This allows a monospaced + * font to vary the side bearing + * values for each glyph. */ +/*UnsizedArrayOfadvancesX;*/ + /* TODO Document. */ public: - DEFINE_SIZE_ARRAY2 (0, longMetric, leadingBearingX); + DEFINE_SIZE_ARRAY (0, longMetricZ); }; -struct hmtx : hmtxvmtx { - static const hb_tag_t tableTag = HB_OT_TAG_hmtx; - static const hb_tag_t headerTag = HB_OT_TAG_hhea; - static const hb_tag_t variationsTag = HB_OT_TAG_HVAR; - static const hb_tag_t os2Tag = HB_OT_TAG_os2; +struct hmtx : hmtxvmtx { + static constexpr hb_tag_t tableTag = HB_OT_TAG_hmtx; + static constexpr hb_tag_t variationsTag = HB_OT_TAG_HVAR; + static constexpr bool is_horizontal = true; }; -struct vmtx : hmtxvmtx { - static const hb_tag_t tableTag = HB_OT_TAG_vmtx; - static const hb_tag_t headerTag = HB_OT_TAG_vhea; - static const hb_tag_t variationsTag = HB_OT_TAG_VVAR; - static const hb_tag_t os2Tag = HB_TAG_NONE; +struct vmtx : hmtxvmtx { + static constexpr hb_tag_t tableTag = HB_OT_TAG_vmtx; + static constexpr hb_tag_t variationsTag = HB_OT_TAG_VVAR; + static constexpr bool is_horizontal = false; +}; + +struct hmtx_accelerator_t : hmtx::accelerator_t { + hmtx_accelerator_t (hb_face_t *face) : hmtx::accelerator_t (face) {} +}; +struct vmtx_accelerator_t : vmtx::accelerator_t { + vmtx_accelerator_t (hb_face_t *face) : vmtx::accelerator_t (face) {} }; } /* namespace OT */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-kern-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-ot-kern-table.hh index e07faca63f3..ffa11bc2496 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-ot-kern-table.hh +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-kern-table.hh @@ -27,365 +27,330 @@ #ifndef HB_OT_KERN_TABLE_HH #define HB_OT_KERN_TABLE_HH -#include "hb-open-type-private.hh" - -namespace OT { +#include "hb-aat-layout-kerx-table.hh" /* * kern -- Kerning + * https://docs.microsoft.com/en-us/typography/opentype/spec/kern + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6kern.html */ - #define HB_OT_TAG_kern HB_TAG('k','e','r','n') -struct hb_glyph_pair_t -{ - hb_codepoint_t left; - hb_codepoint_t right; -}; -struct KernPair -{ - inline int get_kerning (void) const - { return value; } +namespace OT { - inline int cmp (const hb_glyph_pair_t &o) const + +template +struct KernSubTableFormat3 +{ + int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const { - int ret = left.cmp (o.left); - if (ret) return ret; - return right.cmp (o.right); + hb_array_t kernValue = kernValueZ.as_array (kernValueCount); + hb_array_t leftClass = StructAfter> (kernValue).as_array (glyphCount); + hb_array_t rightClass = StructAfter> (leftClass).as_array (glyphCount); + hb_array_t kernIndex = StructAfter> (rightClass).as_array (leftClassCount * rightClassCount); + + unsigned int leftC = leftClass[left]; + unsigned int rightC = rightClass[right]; + if (unlikely (leftC >= leftClassCount || rightC >= rightClassCount)) + return 0; + unsigned int i = leftC * rightClassCount + rightC; + return kernValue[kernIndex[i]]; } - inline bool sanitize (hb_sanitize_context_t *c) const + bool apply (AAT::hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + if (!c->plan->requested_kerning) + return false; + + if (header.coverage & header.Backwards) + return false; + + hb_kern_machine_t machine (*this, header.coverage & header.CrossStream); + machine.kern (c->font, c->buffer, c->plan->kern_mask); + + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + c->check_range (kernValueZ, + kernValueCount * sizeof (FWORD) + + glyphCount * 2 + + leftClassCount * rightClassCount)); + } + + protected: + KernSubTableHeader + header; + HBUINT16 glyphCount; /* The number of glyphs in this font. */ + HBUINT8 kernValueCount; /* The number of kerning values. */ + HBUINT8 leftClassCount; /* The number of left-hand classes. */ + HBUINT8 rightClassCount;/* The number of right-hand classes. */ + HBUINT8 flags; /* Set to zero (reserved for future use). */ + UnsizedArrayOf + kernValueZ; /* The kerning values. + * Length kernValueCount. */ +#if 0 + UnsizedArrayOf + leftClass; /* The left-hand classes. + * Length glyphCount. */ + UnsizedArrayOf + rightClass; /* The right-hand classes. + * Length glyphCount. */ + UnsizedArrayOfkernIndex; + /* The indices into the kernValue array. + * Length leftClassCount * rightClassCount */ +#endif + public: + DEFINE_SIZE_ARRAY (KernSubTableHeader::static_size + 6, kernValueZ); +}; + +template +struct KernSubTable +{ + unsigned int get_size () const { return u.header.length; } + unsigned int get_type () const { return u.header.format; } + + int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const + { + switch (get_type ()) { + /* This method hooks up to hb_font_t's get_h_kerning. Only support Format0. */ + case 0: return u.format0.get_kerning (left, right); + default:return 0; + } + } + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + unsigned int subtable_type = get_type (); + TRACE_DISPATCH (this, subtable_type); + switch (subtable_type) { + case 0: return_trace (c->dispatch (u.format0)); +#ifndef HB_NO_AAT_SHAPE + case 1: return_trace (u.header.apple ? c->dispatch (u.format1, std::forward (ds)...) : c->default_return_value ()); +#endif + case 2: return_trace (c->dispatch (u.format2)); +#ifndef HB_NO_AAT_SHAPE + case 3: return_trace (u.header.apple ? c->dispatch (u.format3, std::forward (ds)...) : c->default_return_value ()); +#endif + default: return_trace (c->default_return_value ()); + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!u.header.sanitize (c) || + u.header.length < u.header.min_size || + !c->check_range (this, u.header.length))) return_trace (false); + + return_trace (dispatch (c)); + } + + public: + union { + KernSubTableHeader header; + AAT::KerxSubTableFormat0 format0; + AAT::KerxSubTableFormat1 format1; + AAT::KerxSubTableFormat2 format2; + KernSubTableFormat3 format3; + } u; + public: + DEFINE_SIZE_MIN (KernSubTableHeader::static_size); +}; + + +struct KernOTSubTableHeader +{ + static constexpr bool apple = false; + typedef AAT::ObsoleteTypes Types; + + unsigned tuple_count () const { return 0; } + bool is_horizontal () const { return (coverage & Horizontal); } + + enum Coverage + { + Horizontal = 0x01u, + Minimum = 0x02u, + CrossStream = 0x04u, + Override = 0x08u, + + /* Not supported: */ + Backwards = 0x00u, + Variation = 0x00u, + }; + + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this)); } - protected: - GlyphID left; - GlyphID right; - FWORD value; + public: + HBUINT16 versionZ; /* Unused. */ + HBUINT16 length; /* Length of the subtable (including this header). */ + HBUINT8 format; /* Subtable format. */ + HBUINT8 coverage; /* Coverage bits. */ public: DEFINE_SIZE_STATIC (6); }; -struct KernSubTableFormat0 +struct KernOT : AAT::KerxTable { - inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const - { - hb_glyph_pair_t pair = {left, right}; - int i = pairs.bsearch (pair); - if (i == -1) - return 0; - return pairs[i].get_kerning (); - } + friend struct AAT::KerxTable; - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (pairs.sanitize (c)); - } + static constexpr hb_tag_t tableTag = HB_OT_TAG_kern; + static constexpr unsigned minVersion = 0u; + + typedef KernOTSubTableHeader SubTableHeader; + typedef SubTableHeader::Types Types; + typedef KernSubTable SubTable; protected: - BinSearchArrayOf pairs; /* Array of kerning pairs. */ + HBUINT16 version; /* Version--0x0000u */ + HBUINT16 tableCount; /* Number of subtables in the kerning table. */ + SubTable firstSubTable; /* Subtables. */ public: - DEFINE_SIZE_ARRAY (8, pairs); + DEFINE_SIZE_MIN (4); }; -struct KernClassTable -{ - inline unsigned int get_class (hb_codepoint_t g) const { return classes[g - firstGlyph]; } - inline bool sanitize (hb_sanitize_context_t *c) const +struct KernAATSubTableHeader +{ + static constexpr bool apple = true; + typedef AAT::ObsoleteTypes Types; + + unsigned tuple_count () const { return 0; } + bool is_horizontal () const { return !(coverage & Vertical); } + + enum Coverage + { + Vertical = 0x80u, + CrossStream = 0x40u, + Variation = 0x20u, + + /* Not supported: */ + Backwards = 0x00u, + }; + + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - return_trace (firstGlyph.sanitize (c) && classes.sanitize (c)); + return_trace (c->check_struct (this)); } - protected: - UINT16 firstGlyph; /* First glyph in class range. */ - ArrayOf classes; /* Glyph classes. */ public: - DEFINE_SIZE_ARRAY (4, classes); + HBUINT32 length; /* Length of the subtable (including this header). */ + HBUINT8 coverage; /* Coverage bits. */ + HBUINT8 format; /* Subtable format. */ + HBUINT16 tupleIndex; /* The tuple index (used for variations fonts). + * This value specifies which tuple this subtable covers. + * Note: We don't implement. */ + public: + DEFINE_SIZE_STATIC (8); }; -struct KernSubTableFormat2 +struct KernAAT : AAT::KerxTable { - inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right, const char *end) const - { - unsigned int l = (this+leftClassTable).get_class (left); - unsigned int r = (this+leftClassTable).get_class (left); - unsigned int offset = l * rowWidth + r * sizeof (FWORD); - const FWORD *arr = &(this+array); - if (unlikely ((const void *) arr < (const void *) this || (const void *) arr >= (const void *) end)) - return 0; - const FWORD *v = &StructAtOffset (arr, offset); - if (unlikely ((const void *) v < (const void *) arr || (const void *) (v + 1) > (const void *) end)) - return 0; - return *v; - } + friend struct AAT::KerxTable; - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (rowWidth.sanitize (c) && - leftClassTable.sanitize (c, this) && - rightClassTable.sanitize (c, this) && - array.sanitize (c, this)); - } + static constexpr hb_tag_t tableTag = HB_OT_TAG_kern; + static constexpr unsigned minVersion = 0x00010000u; + + typedef KernAATSubTableHeader SubTableHeader; + typedef SubTableHeader::Types Types; + typedef KernSubTable SubTable; protected: - UINT16 rowWidth; /* The width, in bytes, of a row in the table. */ - OffsetTo - leftClassTable; /* Offset from beginning of this subtable to - * left-hand class table. */ - OffsetTo - rightClassTable;/* Offset from beginning of this subtable to - * right-hand class table. */ - OffsetTo - array; /* Offset from beginning of this subtable to - * the start of the kerning array. */ + HBUINT32 version; /* Version--0x00010000u */ + HBUINT32 tableCount; /* Number of subtables in the kerning table. */ + SubTable firstSubTable; /* Subtables. */ public: DEFINE_SIZE_MIN (8); }; -struct KernSubTable -{ - inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right, const char *end, unsigned int format) const - { - switch (format) { - case 0: return u.format0.get_kerning (left, right); - case 2: return u.format2.get_kerning (left, right, end); - default:return 0; - } - } - - inline bool sanitize (hb_sanitize_context_t *c, unsigned int format) const - { - TRACE_SANITIZE (this); - switch (format) { - case 0: return_trace (u.format0.sanitize (c)); - case 2: return_trace (u.format2.sanitize (c)); - default:return_trace (true); - } - } - - protected: - union { - KernSubTableFormat0 format0; - KernSubTableFormat2 format2; - } u; - public: - DEFINE_SIZE_MIN (0); -}; - - -template -struct KernSubTableWrapper -{ - /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */ - inline const T* thiz (void) const { return static_cast (this); } - - inline bool is_horizontal (void) const - { return (thiz()->coverage & T::COVERAGE_CHECK_FLAGS) == T::COVERAGE_CHECK_HORIZONTAL; } - - inline bool is_override (void) const - { return bool (thiz()->coverage & T::COVERAGE_OVERRIDE_FLAG); } - - inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right, const char *end) const - { return thiz()->subtable.get_kerning (left, right, end, thiz()->format); } - - inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right, const char *end) const - { return is_horizontal () ? get_kerning (left, right, end) : 0; } - - inline unsigned int get_size (void) const { return thiz()->length; } - - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (c->check_struct (thiz()) && - thiz()->length >= thiz()->min_size && - c->check_array (thiz(), 1, thiz()->length) && - thiz()->subtable.sanitize (c, thiz()->format)); - } -}; - -template -struct KernTable -{ - /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */ - inline const T* thiz (void) const { return static_cast (this); } - - inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right, unsigned int table_length) const - { - int v = 0; - const typename T::SubTableWrapper *st = CastP (thiz()->data); - unsigned int count = thiz()->nTables; - for (unsigned int i = 0; i < count; i++) - { - if (st->is_override ()) - v = 0; - v += st->get_h_kerning (left, right, table_length + (const char *) this); - st = &StructAfter (*st); - } - return v; - } - - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - if (unlikely (!c->check_struct (thiz()) || - thiz()->version != T::VERSION)) - return_trace (false); - - const typename T::SubTableWrapper *st = CastP (thiz()->data); - unsigned int count = thiz()->nTables; - for (unsigned int i = 0; i < count; i++) - { - if (unlikely (!st->sanitize (c))) - return_trace (false); - st = &StructAfter (*st); - } - - return_trace (true); - } -}; - -struct KernOT : KernTable -{ - friend struct KernTable; - - static const uint16_t VERSION = 0x0000u; - - struct SubTableWrapper : KernSubTableWrapper - { - friend struct KernSubTableWrapper; - - enum coverage_flags_t { - COVERAGE_DIRECTION_FLAG = 0x01u, - COVERAGE_MINIMUM_FLAG = 0x02u, - COVERAGE_CROSSSTREAM_FLAG = 0x04u, - COVERAGE_OVERRIDE_FLAG = 0x08u, - - COVERAGE_VARIATION_FLAG = 0x00u, /* Not supported. */ - - COVERAGE_CHECK_FLAGS = 0x07u, - COVERAGE_CHECK_HORIZONTAL = 0x01u - }; - - protected: - UINT16 versionZ; /* Unused. */ - UINT16 length; /* Length of the subtable (including this header). */ - UINT8 format; /* Subtable format. */ - UINT8 coverage; /* Coverage bits. */ - KernSubTable subtable; /* Subtable data. */ - public: - DEFINE_SIZE_MIN (6); - }; - - protected: - UINT16 version; /* Version--0x0000u */ - UINT16 nTables; /* Number of subtables in the kerning table. */ - UINT8 data[VAR]; - public: - DEFINE_SIZE_ARRAY (4, data); -}; - -struct KernAAT : KernTable -{ - friend struct KernTable; - - static const uint32_t VERSION = 0x00010000u; - - struct SubTableWrapper : KernSubTableWrapper - { - friend struct KernSubTableWrapper; - - enum coverage_flags_t { - COVERAGE_DIRECTION_FLAG = 0x80u, - COVERAGE_CROSSSTREAM_FLAG = 0x40u, - COVERAGE_VARIATION_FLAG = 0x20u, - - COVERAGE_OVERRIDE_FLAG = 0x00u, /* Not supported. */ - - COVERAGE_CHECK_FLAGS = 0xE0u, - COVERAGE_CHECK_HORIZONTAL = 0x00u - }; - - protected: - UINT32 length; /* Length of the subtable (including this header). */ - UINT8 coverage; /* Coverage bits. */ - UINT8 format; /* Subtable format. */ - UINT16 tupleIndex; /* The tuple index (used for variations fonts). - * This value specifies which tuple this subtable covers. */ - KernSubTable subtable; /* Subtable data. */ - public: - DEFINE_SIZE_MIN (8); - }; - - protected: - UINT32 version; /* Version--0x00010000u */ - UINT32 nTables; /* Number of subtables in the kerning table. */ - UINT8 data[VAR]; - public: - DEFINE_SIZE_ARRAY (8, data); -}; - struct kern { - static const hb_tag_t tableTag = HB_OT_TAG_kern; + static constexpr hb_tag_t tableTag = HB_OT_TAG_kern; - inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right, unsigned int table_length) const + bool has_data () const { return u.version32; } + unsigned get_type () const { return u.major; } + + bool has_state_machine () const { - switch (u.major) { - case 0: return u.ot.get_h_kerning (left, right, table_length); - case 1: return u.aat.get_h_kerning (left, right, table_length); + switch (get_type ()) { + case 0: return u.ot.has_state_machine (); +#ifndef HB_NO_AAT_SHAPE + case 1: return u.aat.has_state_machine (); +#endif + default:return false; + } + } + + bool has_cross_stream () const + { + switch (get_type ()) { + case 0: return u.ot.has_cross_stream (); +#ifndef HB_NO_AAT_SHAPE + case 1: return u.aat.has_cross_stream (); +#endif + default:return false; + } + } + + int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right) const + { + switch (get_type ()) { + case 0: return u.ot.get_h_kerning (left, right); +#ifndef HB_NO_AAT_SHAPE + case 1: return u.aat.get_h_kerning (left, right); +#endif default:return 0; } } - inline bool sanitize (hb_sanitize_context_t *c) const + bool apply (AAT::hb_aat_apply_context_t *c) const + { return dispatch (c); } + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - TRACE_SANITIZE (this); - if (!u.major.sanitize (c)) return_trace (false); - switch (u.major) { - case 0: return_trace (u.ot.sanitize (c)); - case 1: return_trace (u.aat.sanitize (c)); - default:return_trace (true); + unsigned int subtable_type = get_type (); + TRACE_DISPATCH (this, subtable_type); + switch (subtable_type) { + case 0: return_trace (c->dispatch (u.ot, std::forward (ds)...)); +#ifndef HB_NO_AAT_SHAPE + case 1: return_trace (c->dispatch (u.aat, std::forward (ds)...)); +#endif + default: return_trace (c->default_return_value ()); } } - struct accelerator_t + bool sanitize (hb_sanitize_context_t *c) const { - inline void init (hb_face_t *face) - { - blob = Sanitizer::sanitize (face->reference_table (HB_OT_TAG_kern)); - table = Sanitizer::lock_instance (blob); - table_length = hb_blob_get_length (blob); - } - inline void fini (void) - { - hb_blob_destroy (blob); - } - - inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right) const - { return table->get_h_kerning (left, right, table_length); } - - private: - hb_blob_t *blob; - const kern *table; - unsigned int table_length; - }; + TRACE_SANITIZE (this); + if (!u.version32.sanitize (c)) return_trace (false); + return_trace (dispatch (c)); + } protected: union { - UINT16 major; + HBUINT32 version32; + HBUINT16 major; KernOT ot; +#ifndef HB_NO_AAT_SHAPE KernAAT aat; +#endif } u; public: - DEFINE_SIZE_UNION (2, major); + DEFINE_SIZE_UNION (4, version32); }; } /* namespace OT */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-layout-base-table.hh b/src/3rdparty/harfbuzz-ng/src/hb-ot-layout-base-table.hh new file mode 100644 index 00000000000..2b7e9e4b184 --- /dev/null +++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-layout-base-table.hh @@ -0,0 +1,524 @@ +/* + * Copyright © 2016 Elie Roux + * Copyright © 2018 Google, Inc. + * Copyright © 2018-2019 Ebrahim Byagowi + * + * 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): Behdad Esfahbod + */ + +#ifndef HB_OT_LAYOUT_BASE_TABLE_HH +#define HB_OT_LAYOUT_BASE_TABLE_HH + +#include "hb-open-type.hh" +#include "hb-ot-layout-common.hh" + +namespace OT { + +/* + * BASE -- Baseline + * https://docs.microsoft.com/en-us/typography/opentype/spec/base + */ + +struct BaseCoordFormat1 +{ + hb_position_t get_coord (hb_font_t *font, hb_direction_t direction) const + { + return HB_DIRECTION_IS_HORIZONTAL (direction) ? font->em_scale_y (coordinate) : font->em_scale_x (coordinate); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + FWORD coordinate; /* X or Y value, in design units */ + public: + DEFINE_SIZE_STATIC (4); +}; + +struct BaseCoordFormat2 +{ + hb_position_t get_coord (hb_font_t *font, hb_direction_t direction) const + { + /* TODO */ + return HB_DIRECTION_IS_HORIZONTAL (direction) ? font->em_scale_y (coordinate) : font->em_scale_x (coordinate); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 2 */ + FWORD coordinate; /* X or Y value, in design units */ + HBGlyphID16 referenceGlyph; /* Glyph ID of control glyph */ + HBUINT16 coordPoint; /* Index of contour point on the + * reference glyph */ + public: + DEFINE_SIZE_STATIC (8); +}; + +struct BaseCoordFormat3 +{ + hb_position_t get_coord (hb_font_t *font, + const VariationStore &var_store, + hb_direction_t direction) const + { + const Device &device = this+deviceTable; + + return HB_DIRECTION_IS_HORIZONTAL (direction) + ? font->em_scale_y (coordinate) + device.get_y_delta (font, var_store) + : font->em_scale_x (coordinate) + device.get_x_delta (font, var_store); + } + + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + deviceTable.sanitize (c, this))); + } + + protected: + HBUINT16 format; /* Format identifier--format = 3 */ + FWORD coordinate; /* X or Y value, in design units */ + Offset16To + deviceTable; /* Offset to Device table for X or + * Y value, from beginning of + * BaseCoord table (may be NULL). */ + public: + DEFINE_SIZE_STATIC (6); +}; + +struct BaseCoord +{ + bool has_data () const { return u.format; } + + hb_position_t get_coord (hb_font_t *font, + const VariationStore &var_store, + hb_direction_t direction) const + { + switch (u.format) { + case 1: return u.format1.get_coord (font, direction); + case 2: return u.format2.get_coord (font, direction); + case 3: return u.format3.get_coord (font, var_store, direction); + default:return 0; + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!u.format.sanitize (c))) return_trace (false); + switch (u.format) { + case 1: return_trace (u.format1.sanitize (c)); + case 2: return_trace (u.format2.sanitize (c)); + case 3: return_trace (u.format3.sanitize (c)); + default:return_trace (false); + } + } + + protected: + union { + HBUINT16 format; + BaseCoordFormat1 format1; + BaseCoordFormat2 format2; + BaseCoordFormat3 format3; + } u; + public: + DEFINE_SIZE_UNION (2, format); +}; + +struct FeatMinMaxRecord +{ + int cmp (hb_tag_t key) const { return tag.cmp (key); } + + bool has_data () const { return tag; } + + void get_min_max (const BaseCoord **min, const BaseCoord **max) const + { + if (likely (min)) *min = &(this+minCoord); + if (likely (max)) *max = &(this+maxCoord); + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + minCoord.sanitize (c, base) && + maxCoord.sanitize (c, base))); + } + + protected: + Tag tag; /* 4-byte feature identification tag--must + * match feature tag in FeatureList */ + Offset16To + minCoord; /* Offset to BaseCoord table that defines + * the minimum extent value, from beginning + * of MinMax table (may be NULL) */ + Offset16To + maxCoord; /* Offset to BaseCoord table that defines + * the maximum extent value, from beginning + * of MinMax table (may be NULL) */ + public: + DEFINE_SIZE_STATIC (8); +}; + +struct MinMax +{ + void get_min_max (hb_tag_t feature_tag, + const BaseCoord **min, + const BaseCoord **max) const + { + const FeatMinMaxRecord &minMaxCoord = featMinMaxRecords.bsearch (feature_tag); + if (minMaxCoord.has_data ()) + minMaxCoord.get_min_max (min, max); + else + { + if (likely (min)) *min = &(this+minCoord); + if (likely (max)) *max = &(this+maxCoord); + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + minCoord.sanitize (c, this) && + maxCoord.sanitize (c, this) && + featMinMaxRecords.sanitize (c, this))); + } + + protected: + Offset16To + minCoord; /* Offset to BaseCoord table that defines + * minimum extent value, from the beginning + * of MinMax table (may be NULL) */ + Offset16To + maxCoord; /* Offset to BaseCoord table that defines + * maximum extent value, from the beginning + * of MinMax table (may be NULL) */ + SortedArray16Of + featMinMaxRecords; + /* Array of FeatMinMaxRecords, in alphabetical + * order by featureTableTag */ + public: + DEFINE_SIZE_ARRAY (6, featMinMaxRecords); +}; + +struct BaseValues +{ + const BaseCoord &get_base_coord (int baseline_tag_index) const + { + if (baseline_tag_index == -1) baseline_tag_index = defaultIndex; + return this+baseCoords[baseline_tag_index]; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + baseCoords.sanitize (c, this))); + } + + protected: + Index defaultIndex; /* Index number of default baseline for this + * script — equals index position of baseline tag + * in baselineTags array of the BaseTagList */ + Array16OfOffset16To + baseCoords; /* Number of BaseCoord tables defined — should equal + * baseTagCount in the BaseTagList + * + * Array of offsets to BaseCoord tables, from beginning of + * BaseValues table — order matches baselineTags array in + * the BaseTagList */ + public: + DEFINE_SIZE_ARRAY (4, baseCoords); +}; + +struct BaseLangSysRecord +{ + int cmp (hb_tag_t key) const { return baseLangSysTag.cmp (key); } + + bool has_data () const { return baseLangSysTag; } + + const MinMax &get_min_max () const { return this+minMax; } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + minMax.sanitize (c, base))); + } + + protected: + Tag baseLangSysTag; /* 4-byte language system identification tag */ + Offset16To + minMax; /* Offset to MinMax table, from beginning + * of BaseScript table */ + public: + DEFINE_SIZE_STATIC (6); +}; + +struct BaseScript +{ + const MinMax &get_min_max (hb_tag_t language_tag) const + { + const BaseLangSysRecord& record = baseLangSysRecords.bsearch (language_tag); + return record.has_data () ? record.get_min_max () : this+defaultMinMax; + } + + const BaseCoord &get_base_coord (int baseline_tag_index) const + { return (this+baseValues).get_base_coord (baseline_tag_index); } + + bool has_values () const { return baseValues; } + bool has_min_max () const { return defaultMinMax; /* TODO What if only per-language is present? */ } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + baseValues.sanitize (c, this) && + defaultMinMax.sanitize (c, this) && + baseLangSysRecords.sanitize (c, this))); + } + + protected: + Offset16To + baseValues; /* Offset to BaseValues table, from beginning + * of BaseScript table (may be NULL) */ + Offset16To + defaultMinMax; /* Offset to MinMax table, from beginning of + * BaseScript table (may be NULL) */ + SortedArray16Of + baseLangSysRecords; + /* Number of BaseLangSysRecords + * defined — may be zero (0) */ + + public: + DEFINE_SIZE_ARRAY (6, baseLangSysRecords); +}; + +struct BaseScriptList; +struct BaseScriptRecord +{ + int cmp (hb_tag_t key) const { return baseScriptTag.cmp (key); } + + bool has_data () const { return baseScriptTag; } + + const BaseScript &get_base_script (const BaseScriptList *list) const + { return list+baseScript; } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + baseScript.sanitize (c, base))); + } + + protected: + Tag baseScriptTag; /* 4-byte script identification tag */ + Offset16To + baseScript; /* Offset to BaseScript table, from beginning + * of BaseScriptList */ + + public: + DEFINE_SIZE_STATIC (6); +}; + +struct BaseScriptList +{ + const BaseScript &get_base_script (hb_tag_t script) const + { + const BaseScriptRecord *record = &baseScriptRecords.bsearch (script); + if (!record->has_data ()) record = &baseScriptRecords.bsearch (HB_TAG ('D','F','L','T')); + return record->has_data () ? record->get_base_script (this) : Null (BaseScript); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + baseScriptRecords.sanitize (c, this)); + } + + protected: + SortedArray16Of + baseScriptRecords; + + public: + DEFINE_SIZE_ARRAY (2, baseScriptRecords); +}; + +struct Axis +{ + bool get_baseline (hb_tag_t baseline_tag, + hb_tag_t script_tag, + hb_tag_t language_tag, + const BaseCoord **coord) const + { + const BaseScript &base_script = (this+baseScriptList).get_base_script (script_tag); + if (!base_script.has_values ()) + { + *coord = nullptr; + return false; + } + + if (likely (coord)) + { + unsigned int tag_index = 0; + if (!(this+baseTagList).bfind (baseline_tag, &tag_index)) + { + *coord = nullptr; + return false; + } + *coord = &base_script.get_base_coord (tag_index); + } + + return true; + } + + bool get_min_max (hb_tag_t script_tag, + hb_tag_t language_tag, + hb_tag_t feature_tag, + const BaseCoord **min_coord, + const BaseCoord **max_coord) const + { + const BaseScript &base_script = (this+baseScriptList).get_base_script (script_tag); + if (!base_script.has_min_max ()) + { + *min_coord = *max_coord = nullptr; + return false; + } + + base_script.get_min_max (language_tag).get_min_max (feature_tag, min_coord, max_coord); + + return true; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + baseTagList.sanitize (c, this) && + baseScriptList.sanitize (c, this))); + } + + protected: + Offset16To> + baseTagList; /* Offset to BaseTagList table, from beginning + * of Axis table (may be NULL) + * Array of 4-byte baseline identification tags — must + * be in alphabetical order */ + Offset16To + baseScriptList; /* Offset to BaseScriptList table, from beginning + * of Axis table + * Array of BaseScriptRecords, in alphabetical order + * by baseScriptTag */ + + public: + DEFINE_SIZE_STATIC (4); +}; + +struct BASE +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_BASE; + + const Axis &get_axis (hb_direction_t direction) const + { return HB_DIRECTION_IS_VERTICAL (direction) ? this+vAxis : this+hAxis; } + + const VariationStore &get_var_store () const + { return version.to_int () < 0x00010001u ? Null (VariationStore) : this+varStore; } + + bool get_baseline (hb_font_t *font, + hb_tag_t baseline_tag, + hb_direction_t direction, + hb_tag_t script_tag, + hb_tag_t language_tag, + hb_position_t *base) const + { + const BaseCoord *base_coord = nullptr; + if (unlikely (!get_axis (direction).get_baseline (baseline_tag, script_tag, language_tag, &base_coord) || + !base_coord || !base_coord->has_data ())) + return false; + + if (likely (base)) + *base = base_coord->get_coord (font, get_var_store (), direction); + + return true; + } + + bool get_min_max (hb_font_t *font, + hb_direction_t direction, + hb_tag_t script_tag, + hb_tag_t language_tag, + hb_tag_t feature_tag, + hb_position_t *min, + hb_position_t *max) const + { + const BaseCoord *min_coord, *max_coord; + if (!get_axis (direction).get_min_max (script_tag, language_tag, feature_tag, + &min_coord, &max_coord)) + return false; + + const VariationStore &var_store = get_var_store (); + if (likely (min && min_coord)) *min = min_coord->get_coord (font, var_store, direction); + if (likely (max && max_coord)) *max = max_coord->get_coord (font, var_store, direction); + return true; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + likely (version.major == 1) && + hAxis.sanitize (c, this) && + vAxis.sanitize (c, this) && + (version.to_int () < 0x00010001u || varStore.sanitize (c, this)))); + } + + protected: + FixedVersion<>version; /* Version of the BASE table */ + Offset16TohAxis; /* Offset to horizontal Axis table, from beginning + * of BASE table (may be NULL) */ + Offset16TovAxis; /* Offset to vertical Axis table, from beginning + * of BASE table (may be NULL) */ + Offset32To + varStore; /* Offset to the table of Item Variation + * Store--from beginning of BASE + * header (may be NULL). Introduced + * in version 0x00010001. */ + public: + DEFINE_SIZE_MIN (8); +}; + + +} /* namespace OT */ + + +#endif /* HB_OT_LAYOUT_BASE_TABLE_HH */ diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-layout-common-private.hh b/src/3rdparty/harfbuzz-ng/src/hb-ot-layout-common-private.hh deleted file mode 100644 index 5e699e1967f..00000000000 --- a/src/3rdparty/harfbuzz-ng/src/hb-ot-layout-common-private.hh +++ /dev/null @@ -1,1772 +0,0 @@ -/* - * Copyright © 2007,2008,2009 Red Hat, Inc. - * Copyright © 2010,2012 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. - * - * Red Hat Author(s): Behdad Esfahbod - * Google Author(s): Behdad Esfahbod - */ - -#ifndef HB_OT_LAYOUT_COMMON_PRIVATE_HH -#define HB_OT_LAYOUT_COMMON_PRIVATE_HH - -#include "hb-private.hh" -#include "hb-debug.hh" -#include "hb-ot-layout-private.hh" -#include "hb-open-type-private.hh" -#include "hb-set-private.hh" - - -#ifndef HB_MAX_NESTING_LEVEL -#define HB_MAX_NESTING_LEVEL 6 -#endif -#ifndef HB_MAX_CONTEXT_LENGTH -#define HB_MAX_CONTEXT_LENGTH 64 -#endif - - -namespace OT { - - -#define NOT_COVERED ((unsigned int) -1) - - - -/* - * - * OpenType Layout Common Table Formats - * - */ - - -/* - * Script, ScriptList, LangSys, Feature, FeatureList, Lookup, LookupList - */ - -template -struct Record -{ - inline int cmp (hb_tag_t a) const { - return tag.cmp (a); - } - - struct sanitize_closure_t { - hb_tag_t tag; - const void *list_base; - }; - inline bool sanitize (hb_sanitize_context_t *c, const void *base) const - { - TRACE_SANITIZE (this); - const sanitize_closure_t closure = {tag, base}; - return_trace (c->check_struct (this) && offset.sanitize (c, base, &closure)); - } - - Tag tag; /* 4-byte Tag identifier */ - OffsetTo - offset; /* Offset from beginning of object holding - * the Record */ - public: - DEFINE_SIZE_STATIC (6); -}; - -template -struct RecordArrayOf : SortedArrayOf > { - inline const Tag& get_tag (unsigned int i) const - { - /* We cheat slightly and don't define separate Null objects - * for Record types. Instead, we return the correct Null(Tag) - * here. */ - if (unlikely (i >= this->len)) return Null(Tag); - return (*this)[i].tag; - } - inline unsigned int get_tags (unsigned int start_offset, - unsigned int *record_count /* IN/OUT */, - hb_tag_t *record_tags /* OUT */) const - { - if (record_count) { - const Record *arr = this->sub_array (start_offset, record_count); - unsigned int count = *record_count; - for (unsigned int i = 0; i < count; i++) - record_tags[i] = arr[i].tag; - } - return this->len; - } - inline bool find_index (hb_tag_t tag, unsigned int *index) const - { - /* If we want to allow non-sorted data, we can lsearch(). */ - int i = this->/*lsearch*/bsearch (tag); - if (i != -1) { - if (index) *index = i; - return true; - } else { - if (index) *index = Index::NOT_FOUND_INDEX; - return false; - } - } -}; - -template -struct RecordListOf : RecordArrayOf -{ - inline const Type& operator [] (unsigned int i) const - { return this+RecordArrayOf::operator [](i).offset; } - - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (RecordArrayOf::sanitize (c, this)); - } -}; - - -struct RangeRecord -{ - inline int cmp (hb_codepoint_t g) const { - return g < start ? -1 : g <= end ? 0 : +1 ; - } - - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (c->check_struct (this)); - } - - inline bool intersects (const hb_set_t *glyphs) const { - return glyphs->intersects (start, end); - } - - template - inline bool add_coverage (set_t *glyphs) const { - return glyphs->add_range (start, end); - } - - GlyphID start; /* First GlyphID in the range */ - GlyphID end; /* Last GlyphID in the range */ - UINT16 value; /* Value */ - public: - DEFINE_SIZE_STATIC (6); -}; -DEFINE_NULL_DATA (RangeRecord, "\000\001"); - - -struct IndexArray : ArrayOf -{ - inline unsigned int get_indexes (unsigned int start_offset, - unsigned int *_count /* IN/OUT */, - unsigned int *_indexes /* OUT */) const - { - if (_count) { - const UINT16 *arr = this->sub_array (start_offset, _count); - unsigned int count = *_count; - for (unsigned int i = 0; i < count; i++) - _indexes[i] = arr[i]; - } - return this->len; - } -}; - - -struct Script; -struct LangSys; -struct Feature; - - -struct LangSys -{ - inline unsigned int get_feature_count (void) const - { return featureIndex.len; } - inline hb_tag_t get_feature_index (unsigned int i) const - { return featureIndex[i]; } - inline unsigned int get_feature_indexes (unsigned int start_offset, - unsigned int *feature_count /* IN/OUT */, - unsigned int *feature_indexes /* OUT */) const - { return featureIndex.get_indexes (start_offset, feature_count, feature_indexes); } - - inline bool has_required_feature (void) const { return reqFeatureIndex != 0xFFFFu; } - inline unsigned int get_required_feature_index (void) const - { - if (reqFeatureIndex == 0xFFFFu) - return Index::NOT_FOUND_INDEX; - return reqFeatureIndex;; - } - - inline bool sanitize (hb_sanitize_context_t *c, - const Record::sanitize_closure_t * = nullptr) const - { - TRACE_SANITIZE (this); - return_trace (c->check_struct (this) && featureIndex.sanitize (c)); - } - - Offset16 lookupOrderZ; /* = Null (reserved for an offset to a - * reordering table) */ - UINT16 reqFeatureIndex;/* Index of a feature required for this - * language system--if no required features - * = 0xFFFFu */ - IndexArray featureIndex; /* Array of indices into the FeatureList */ - public: - DEFINE_SIZE_ARRAY (6, featureIndex); -}; -DEFINE_NULL_DATA (LangSys, "\0\0\xFF\xFF"); - - -struct Script -{ - inline unsigned int get_lang_sys_count (void) const - { return langSys.len; } - inline const Tag& get_lang_sys_tag (unsigned int i) const - { return langSys.get_tag (i); } - inline unsigned int get_lang_sys_tags (unsigned int start_offset, - unsigned int *lang_sys_count /* IN/OUT */, - hb_tag_t *lang_sys_tags /* OUT */) const - { return langSys.get_tags (start_offset, lang_sys_count, lang_sys_tags); } - inline const LangSys& get_lang_sys (unsigned int i) const - { - if (i == Index::NOT_FOUND_INDEX) return get_default_lang_sys (); - return this+langSys[i].offset; - } - inline bool find_lang_sys_index (hb_tag_t tag, unsigned int *index) const - { return langSys.find_index (tag, index); } - - inline bool has_default_lang_sys (void) const { return defaultLangSys != 0; } - inline const LangSys& get_default_lang_sys (void) const { return this+defaultLangSys; } - - inline bool sanitize (hb_sanitize_context_t *c, - const Record